Word macro for reformatting an index entry - vba

I have a Word document using font size 8 in which I manually have created some index objects using the shortcut Shift-Alt-X. It might look like this, showing formatting symbols:
some·words·in·bold{·XE·"bold"},·some·in·italic{·XE·"italic"}
Actually, the "XE" is set with font size 11 (my normal standard), but I can't reproduce that here. So I have written a macro which intends to normalize the index object when called from just after the ”}”-sign. It extends the range backwards to include the ”{”-sign and resets the selection to standard values:
Private Sub NormalizeEntry()
Dim rng As Range
Set rng = Selection.Range
With rng
.MoveStartUntil Cset:="{", Count:=-100
.MoveStart Unit:=wdCharacter, Count:=-1
.Select
.Font.Size = 8
.Font.ColorIndex = wdBlack
.Font.Bold = False
.Font.Italic = False
.Select
End With
End Sub
The macro works fine if I apply it after a standard sequence of words enclosed in brackets like
Behold, {here are some words enclosed in brackets} and more words...
("brackets" written with a colored font, which I also can't reproduce here), but it fails when used after an XE-entry. The entry gets selectet all right, but the font is not changed. What am I missing here?

As John advises, you should set the font attributes of the underlying Style to 8pt. As for the bold & italic formatting:
Bold should be applied via the Strong character Style; and
Italic should be applied via the Emphasis character Style.
To revert anything with the wrong format to that of the underlying paragraph Style, simply select the offending content and press Ctrl-Space.
Here's a way of achieving the same thing for all fields via a macro:
Sub FieldReset()
Application.ScreenUpdating = False
Dim Fld As Field, Rng As Range
For Each Fld In ActiveDocument.Fields
With Fld
Set Rng = .Code
With Rng
With .Duplicate
Do While .Fields.Count = 0
.Start = .Start - 1
Loop
Rng.Start = .Start + 1
End With
Do While .Fields.Count = 0
.End = .End + 1
Loop
.Start = .Start - 1
.Font.Reset
End With
End With
Next
Application.ScreenUpdating = True
End Sub

Thank you, Macropod, for your input. However your solution is not what I am looking for, I want to format the field upon insertion.
By experimenting I found a useful solution: Don’t use ActiveDocument.Indexes.MarkEntry, but construct the XE-field yourself using rng.Fields.Add. Here is my result:
Sub TextToNormal(rng As Range)
With rng.Font
.Bold = False
.Italic = False
.Underline = wdUnderlineNone
.StrikeThrough = False
.DoubleStrikeThrough = False
.Color = wdColorAutomatic
.Engrave = False
.Superscript = False
.Subscript = False
End With
End Sub
Function SkipSurroundingSpaces(rng As Range) As Range
With rng
.MoveStartWhile Cset:=" ", Count:=wdForward
.MoveEndWhile Cset:=" ", Count:=wdBackward
.Select
End With
End Function
Sub TestSkipSurroundingSpaces()
Dim rng As Range
Set rng = Selection.Range
SkipSurroundingSpaces rng
End Sub
Sub InsertXEfield()
Dim Xref As String
Dim rng As Range
Set rng = Selection.Range
With rng
SkipSurroundingSpaces rng
Xref = .Text
.Collapse wdCollapseEnd
.Fields.Add Range:=rng, Type:=wdFieldEmpty, _
PreserveFormatting:=False
.Select
.MoveEnd Unit:=wdCharacter, Count:=4
Debug.Print .Start, .End
TextToNormal rng
.MoveStart Unit:=wdCharacter, Count:=2
Debug.Print .Start, .End
.Collapse wdCollapseStart
.InsertAfter "XE """ & Xref & """"
.MoveStart Unit:=wdCharacter, Count:=4
.MoveEnd Unit:=wdCharacter, Count:=-1
.Italic = True
.Bold = True
.Font.ColorIndex = wdViolet
End With
End Sub
Comments:
When selecting a word the range could include a trailing space. This is removed by a call to SkipSurroundingSpaces. Next the selected word is read (actually I take Xref from a form’s inputbox, where it can be changed to something more appropriate, say changing “exoplanets” to “exoplanet”). The range is collapsed to its end and an empty field is inserted. This field might have inherrited formatting from the entry, so it is set back to normal by calling TextToNormal. Then the range is moved into the field (passing by “{·”) and the XE-text is inserted. Finally the range is reduced to include the inserted word only and this word can now be formatted to your heart’s content or as instructed by information from a user form.
(Code and comments updated 2022-06-08)

Related

Place words in a given font in index entries in a document at location of marked words

Goodevening everybody,
I made a VBA code which loops through all the words in a document and checks if the used font of that word is SimSun. If the font is SimSun, the word should be marked for the overall index. So I made this code:
Sub toevoegen()
Dim doc As Document
Set doc = ActiveDocument
For Each sentence In doc.StoryRanges
For Each w In sentence.Words
If w.Font.Name = "SimSun" Then
doc.Indexes.MarkEntry Range:=Selection.Range, Entry:=w
End If
Next
Next
End Sub
The code works, but there is one problem. The index entries are placed at the end of the document. I want them to be placed after the words which where marked. So this is the result when you run the code:
And I want it to be after the word SimSun and Previous. I am stuck. Can somebody help me?
Using Find/Replace is likely to be far quicker than looping through every 'sentence':
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long, StrIdx As String
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Forward = True
.Format = False
.Wrap = wdFindStop
.Font.Name = "SimSun"
End With
Do While .Find.Execute
StrIdx = .Text
.Collapse wdCollapseEnd
.Fields.Add .Duplicate, wdFieldEmpty, "XE " & StrIdx, False
.MoveEndUntil Chr(21), wdForward
.End = .End + 1
.Font.Reset
If .Information(wdWithInTable) = True Then
If .End = .Cells(1).Range.End - 1 Then
.End = .Cells(1).Range.End
.Collapse wdCollapseEnd
If .Information(wdAtEndOfRowMarker) = True Then
.End = .End + 1
End If
End If
End If
If .End = ActiveDocument.Range.End Then Exit Do
.Collapse wdCollapseEnd
Loop
End With
Application.ScreenUpdating = True
End Sub
It is putting the index entry at your selection point.
You can move the selection point.
Try:
Sub toevoegen()
Dim doc As Document
Set doc = ActiveDocument
For Each sentence In doc.StoryRanges
For Each w In sentence.Words
If w.Font.Name = "SimSun" Then
w.Select
Selection.Collapse (wdCollapseEnd)
doc.Indexes.MarkEntry Range:=Selection.Range, Entry:=w
End If
Next
Next
End Sub
That will insert the index entry just after the target word. Running it multiple times will result in multiple entries for each word.

How to tag all blank DOCPROPERTY fields?

I'm trying to find and tag all blank DOCPROPERTY fields in a selection of text.
The macro will tag some of the required fields with the text, but not others.
The macro then fails with run-time error 5941.
I have a feeling it has something to do with how the loop is counting and cycling through blank fields.
Sub RemoveFieldCodes()
'Moves to start of doc and selects text block
Selection.HomeKey Unit:=wdStory
Selection.HomeKey Unit:=wdStory
Selection.MoveDown Unit:=wdLine, Count:=2
Selection.MoveDown Unit:=wdParagraph, Count:=6, Extend:=wdExtend
'Finds blank fields and appends text to end
Dim i As Long
With Selection
For i = Selection.Fields.Count To 1 Step -1
If .Fields(i).Type = wdFieldDocProperty And .Result = "" Then
.Fields(i).Select
Selection.EndKey Unit:=wdLine
Selection.TypeText Text:="BLANK"
End If
Next i
End With
You should work backwards through the collection; otherwise consecutive blanks are liable to be missed. Try:
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long, Rng As Range
With ActiveDocument
For i = .Fields.Count To 1 Step -1
With .Fields(i)
If .Type = wdFieldDocProperty Then
If .Result = " " Then
Set Rng = .Result
.Delete
Rng.Text = "BLANK"
End If
End If
End With
Next
End With
Application.ScreenUpdating = True
End Sub
It's not clear why you're using 'Selection.EndKey Unit:=wdLine' so, if:
Rng.Text = "BLANK"
isn't sufficient, try replacing that with:
With Rng
.End = .Paragraphs(1).Range.End - 1
.Text = "BLANK"
End With

Giving equations specific numbers

I'm new to VBA and I really would appreciate the help. First I should mention that I use OFFICE 2016.
I used Macro recording to create a macro that copies the selected equation then it inserts a table (one row,two columns)and adjust it to remove borders. After that the macro pastes the copied equation in the first cell and moves to the next cell and insert an empty equation (which the user then inserts the desired number of the equation in it).
It works fine except until it reaches the step where it should paste the copied equation. Every time I run the macro I get
Run-time error: 6335
and the macro breaks and when I debug it, this is the line that breaks the procedure:
Selection.PasteAndFormat (wdFormatOriginalFormatting)
after debugging when I hit the continue/run button, it completes the job as required.
Below is the macro I use. Thanks in advance.
Sub MacroEQNUMBER()
'
' MacroEQNUMBER Macro
'
'
Dim rng As Range
If Selection.Range = "" Then
Selection.HomeKey Unit:=wdLine
Selection.EndKey Unit:=wdLine, Extend:=wdExtend
End If
Set rng = Selection.Range
rng.Cut
ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=1, NumColumns:= _
2, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
wdAutoFitFixed
With Selection.Tables(1)
If .Style <> "Table Grid" Then
.Style = "Table Grid"
End If
.ApplyStyleHeadingRows = True
.ApplyStyleLastRow = False
.ApplyStyleFirstColumn = True
.ApplyStyleLastColumn = False
.ApplyStyleRowBands = True
.ApplyStyleColumnBands = False
.Borders.Enable = False
End With
Selection.Tables(1).Cell(1, 1).Range.Select
Selection.TypeText Text:="["
Selection.OMaths.Add Range:=Selection.Range
Selection.MoveRight Unit:=wdCharacter, Count:=2
Selection.MoveLeft Unit:=wdCharacter, Count:=1
Selection.TypeText Text:="]"
Selection.MoveLeft Unit:=wdCharacter, Count:=2
Selection.Tables(1).Columns(1).Cells.Width = 80
Selection.Tables(1).Columns(2).Cells.Width = 350
Selection.Tables(1).Cell(1, 2).Range.Select
Selection.PasteAndFormat (wdFormatOriginalFormatting)
Selection.TypeBackspace
Selection.HomeKey Unit:=wdLine
Selection.MoveLeft Unit:=wdCharacter, Count:=4
End Sub
I get the feeling the problem comes from pasting the equation. Since you're using Selection it's quite possible you're putting the Selection in a table cell structure. Word used to allow this, which would "break" the document. In more recent versions it's simply not allowed, which will result in an error...
Rather than try to put bandaids on your recorded code, I've done my best to interpret what is meant (thank you for the excellent description) and convert it to "object code". The SELECTION object isn't ideal because you can never be sure where it is, nor what is meant. Better is to use the underlying object model, usually RANGE objects.
To begin, the Selection is assigned to a Range - now, no matter where the visible selection is in the document, the Range will never change.
Next, I assign the new table to a Table object. From that, I can get the Range of the first cell and work with it (insert text).
I apparently do need a Selection for inserting the OMath, so I position between the square brackets, select and insert that. Finally, the columns are formatted, the cut equation pasted and the selection positioned at the beginning of the first cell (I think that's what you wanted, but I'm not sure about this last).
Sub MacroEQNUMBER()
'
' MacroEQNUMBER Macro
'
'
Dim rng As Range
Dim tbl As word.Table
Dim cel As word.Cell
Dim rngCell As word.Range
If Selection.Range = "" Then
Selection.HomeKey Unit:=wdLine
Selection.EndKey Unit:=wdLine, Extend:=wdExtend
End If
Set rng = Selection.Range
rng.Cut
Set tbl = ActiveDocument.Tables.Add(Range:=Selection.Range, NumRows:=1, NumColumns:= _
2, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
wdAutoFitFixed)
With tbl
If .Style <> "Table Grid" Then
.Style = "Table Grid"
End If
.ApplyStyleHeadingRows = True
.ApplyStyleLastRow = False
.ApplyStyleFirstColumn = True
.ApplyStyleLastColumn = False
.ApplyStyleRowBands = True
.ApplyStyleColumnBands = False
.Borders.Enable = False
End With
Set cel = tbl.Cell(1, 1)
Set rngCell = cel.Range
rngCell.Text = "[]"
rngCell.Collapse wdCollapseStart
rngCell.MoveStart wdCharacter, 1
rngCell.Select
rngCell.OMaths.Add Range:=rngCell
tbl.Columns(1).Cells.width = 80
tbl.Columns(2).Cells.width = 350
Set rngCell = tbl.Cell(1, 2).Range
rngCell.PasteAndFormat wdFormatOriginalFormatting
rngCell.Characters(rngCell.Characters.Count - 1).Delete
tbl.Cell(1, 1).Select
Selection.Collapse wdCollapseStart
End Sub

VBA - Get the range as Selection

I'm using Range.Find to find a specific string in a document. When I find this string I want to look at the character BEFORE this string. I had an idea to get the range as selection and then use the Selection.MoveLeft = 1 but I really can't find how to get the range as selection. This is the code I have:
Private Function abc() As Boolean
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
MsgBox (rng.Text)
Set Selection = rng 'Set the selection from range
MsgBox (Selection.Text)
Selection.MoveLeft = 1 'Move the selection
MsgBox (Selection.Text)
Loop
End With
abc = True
End Function
Solution
Here is my solution.
Sub testhis()
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
rng.Select
Selection.MoveLeft Unit:=wdCharacter, Count:=2
MsgBox (Selection.Text)
Loop
End With
End Sub
Hope this helps.
Here's a way you can do it without Selecting
Sub abc()
Dim rng As Range
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
MsgBox rng.Text
rng.Move wdCharacter, -2
rng.Expand wdCharacter
MsgBox rng.Text
Loop
End With
End Sub

When using range.find to find bold text it won't find if the entire selection is bold!

I'm trying to extract bold text using the range.find method and all is peachy except if the entire range is actually bold (not likely to happen much, it's more of an edge condition).
With rngFindRange.Find
.ClearFormatting
.Font.Bold = True
Do
.Execute
If Not .Found Then
Exit Do
End If
'do something with found text'
Set rngFindRange = ActiveDocument.Range(rngFindRange.End + 1, Selection.End)
Loop
The above matches bold text right at the start or right at the end, even both but not when the entire range is bold. I think I might have to test the range.font.bold = true before searching through the range. What does stackoverflow think?
This should find any bold text:
Sub SearchBoldText()
Dim rng As Range
Set rng = ThisDocument.Range(0, 0)
With rng.Find
.ClearFormatting
.Format = True
.Font.Bold = True
While .Execute
rng.Select
rng.Collapse direction:=wdCollapseEnd
Wend
End With
Set rng = Nothing
End Sub