VBA Word - iterate through paragraphs to set style - vba

I have a simple script that goes through and sets the style for all paragraphs beginning with a certain character. Easy, I thought. It changes all the paragraphs so they have the same properties as the "Details" Style. But for some reason only the last paragraph ends up with "Details" as its style and all the ones before go back to "Normal". Here's my code so far:
Sub Format_doc()
Dim txt As String
For Each par In ActiveDocument.Paragraphs
txt = par.Range.Text
If Left(txt, 1) = "/" Then
par.Style = "Details"
par.Range.Text = Right(txt, Len(txt) - 1)
End If
Next
End Sub
I'd like to keep them attached to the style because I toggle the "hidden" font property in another macro. I'll need to toggle this hidden property for these paragraphs on-and-off several times and assigning a single paragraph style seemed like an easy solution. Here's the other code:
Sub Toggle_hidden()
ActiveDocument.Styles("Details").Font.Hidden = Not ActiveDocument.Styles("Details").Font.Hidden
End Sub
Solutions? I'm working on Mac, but ultimately this will end up on a Windows.

Your code works fine, here. But perhaps that's due to the version of MacWord... I tested with Office 2016 (Office 365 subscription).
If it's not working for you it may have something to do with the way you're removing the / by basically replacing the paragraph's content. This will also affect the paragraph mark, which is responsible for the paragraph formatting, including the style. Try the following, which explicitly removes the first character and leaves everything else intact:
Sub Format_doc()
Dim txt As String
Dim par As Word.Paragraph
For Each par In ActiveDocument.Paragraphs
txt = par.Range.Text
If Left(txt, 1) = "/" Then
par.Style = "Details"
'par.Range.Text = Right(txt, Len(txt) - 1)
par.Range.Characters(1).Delete
End If
Next
End Sub

Here's a different approach that should also work - and be somewhat faster.
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "^p/"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = False
.Execute
End With
Do While .Find.Found
With .Duplicate
.Start = .Start + 1
.End = .Paragraphs(1).Style = "Details"
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub

Related

Create Word table with text

I would like to reformat an existing and recurring document.
The document is available in two almost identical versions.
The script should search the whole document for the word "ColumnA" and set a starting point for the table behind it.
Then it should search for the word "ContinuousText" or a "Page Break" and create an end point for the table before that.
This is also the difference between the two documents. One has more text (ContinuousText) and the other has no more text but only a "Page Break".
My script works fine when I have "ColumnA" and "ContinuousText".
How do I insert a if there is no "ContinuousText" - looking for "Page Break" query?
This is the scipt that works ("ColumnA" and "ContinuousText")
Sub SlideNoteToTable()
' -----------------< Create Table >-----------------
Dim suchBereich As Range, TabBereich As Range, tabelle As Table
Dim collStart As Collection, collEnd As Collection
Dim d As Long
Set collStart = New Collection: Set collEnd = New Collection
'Collect starting points for the table areas (ColumnA- Ende)
Set suchBereich = ActiveDocument.Range
With suchBereich.Find
.Text = "ColumnA"
Do While .Execute
collStart.Add suchBereich.Paragraphs(1).Range.End + 1
Loop
End With
' Endpunkte für die Tabellenbereiche sammeln (ContinuousText- Text Start)
Set suchBereich = ActiveDocument.Range
With suchBereich.Find
.Text = "ContinuousText"
Do While .Execute
collEnd.Add suchBereich.Start - 1
Loop
End With
'Convert areas to table
For d = collStart.Count To 1 Step -1
Set TabBereich = ActiveDocument.Range(collStart(d), collEnd(d))
Set tabelle = TabBereich.ConvertToTable(Separator:=wdSeparateByParagraphs, NumColumns:=2, AutoFitBehavior:=wdAutoFitWindow)
With tabelle
'all table formatting operations
.AllowAutoFit = False
.PreferredWidthType = wdPreferredWidthPoints
.Borders.Enable = True
.Rows(1).HeadingFormat = True
.Columns(1).SetWidth ColumnWidth:=.PreferredWidth * 2 / 3, RulerStyle:=wdAdjustProportional
End With
Next d
' -----------------< Delete empty tables >-----------------
Dim tabelleX As Table, zeile As Row
For Each tabelleX In ActiveDocument.Tables
For Each zeile In tabelleX.Rows
If Len(zeile.Range) = 4 Then 'there's nothing in it but an empty paragraph mark
zeile.Delete
End If
Next zeile
Next tabelleX
End Sub
I tried to solve the problem pragmatically by simply changing:
.Text = "ContinuousText"
to
.Text = "ContinuousText" Or "^m"
Okay, you can stop laughing now.
I've noticed that it's not quite that easy. But how do I get such an "or" query?
Many thanks for your help.
You cannot use anything like:
.Text = "ContinuousText" Or "^m"
or
.Text = "ContinuousText" Or .Text = "^m"
As a VBA Find expression.
As described in my answer to your other thread on the related issue, you could use two wildcard Find loops, the first being for:
.Text = "ColumnA[!^m]#ContinuousText"
to locate all strings from 'ColumnA' to 'ContinuousText' without an intervening manual page break.
You could then use a separate loop for:
.Text = "ColumnA*^m"
to locate all strings from 'ColumnA' to the next manual page break and couple that with a test on the found range like:
If Instr(.Text, "ContinuousText") = 0 Then
'do the table construction
End If
By putting the code for the table construction in a separate Sub that your main routine calls, you can minimize the code duplication.
I understand your first snippet of code...
.Text = "ContinuousText" Or "^m"
and I have integrated it into the script.
Sub SlideNoteToTable2()
'
' SlideNoteToTable2 Macro
' Formats the speaker text into a two-column table
'
Application.ScreenUpdating = False
' -----------------< Reduce all images proportionally by 40% >-----------------
Dim i As Long
With ActiveDocument.Range
For i = 1 To .InlineShapes.Count
With .InlineShapes(i)
.ScaleHeight = 50
.ScaleWidth = 50
End With
Next i
' ----------------- Locate everything from 'Slide notes' to 'Text Captions' by a Wildcard >-----------------
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "Slide notes[!^m]#Text Captions"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
End With
' ----------------- Replacing the two phrases 'Slide notes & Text Captions' with 'Speaker text: & Screen text:' >-----------------
Do While .Find.Execute
.Paragraphs.First.Range.Text = "Speaker text:"
.Paragraphs.Last.Range.Text = "Screen text:" & vbCr
.Start = .Paragraphs.First.Range.End
.End = .Paragraphs.Last.Range.Start
Do While .Characters.First.Text = vbCr
.Characters.First.Delete
Loop
' ----------------- Converting the intervening content to a two-column table >-----------------
With .Duplicate
.Find.Execute FindText:="^13^13", ReplaceWith:="^t^p", Replace:=wdReplaceAll
.ConvertToTable Separator:=vbTab, NumRows:=.Paragraphs.Count, NumColumns:=2, AutoFitBehavior:=wdAutoFitWindow
With .Tables(1)
.AllowAutoFit = False
.PreferredWidthType = wdPreferredWidthPoints
.Borders.Enable = True
.Rows(1).HeadingFormat = True
.Columns(1).SetWidth ColumnWidth:=.PreferredWidth * 3 / 4, RulerStyle:=wdAdjustProportional
Do While .Range.Characters.Last.Next = vbCr
.Range.Characters.Last.Next.Delete
Loop
End With
End With
.Collapse wdCollapseEnd
Loop
End With
Application.ScreenUpdating = True
End Sub
I don't know what to do with the other two snippets of code and where to put them.
.Text = "ColumnA*^m"
'
If Instr(.Text, "ContinuousText") = 0 Then
'do the table construction
End If
I tried a few things that unfortunately didn't work.
Many thanks for the help,
Kind regards.

Word VBA Range.Find always starts at zero, regardless of the vale in Range.start

I am trying to use Find to step through all occurrences of a particular style. This fragment illustrates the problem.
Public Sub test()
Dim rng As Range
Dim found As Boolean
Set rng = ActiveDocument.Range()
rng.Find.Style = "prod_code"
found = rng.Find.Execute()
rng.Start = rng.End
rng.End = ActiveDocument.Range().End
found = rng.Find.Execute()
End Sub
After the first Execute found is True, rng.start is 152, rng.end is 153 and rng.text is "1". Immediately before the second Execute rng.start and rng.end are 153 and 27219 and the value of rng.text reflects this.
After the second Execute found is true, and start and end are once more 152 and 153.
By searching for text as well as style I have established that the second search is starting at zero, not searching to the end and then wrapping (although it should still wrap within the range).
The end value is taken as given. If I set it before the first match then Execute returns False.
I have tried creating a new Range for the second search but it behaves the same way.
I have tried this with MS Office 2007 and 2016, and with the Execute and Execute2007 methods so, presumably, this behaviour is intended but how should I search a range which does not begin at zero?
I have just realised that the first occurrence of the style I was looking for is in a cell of a table. When I run my test code on a document without tables it behaves as I expected, that is, the second search result was within the range being searched. This gives me a new line of attack but the fundamental problem remains. I want to search for a style within a specific range of a document, regardless of whether or not that range includes a table, or part of a table.
A problem you'll encounter when dong a Find for formatting in tables and you're not specifying the text is that the found range might include the end-of-cell and/or end-of-row markers. In that case, simply collapsing the found range before executing the next 'find' is insufficient. Try something based on:
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Forward = True
.Format = True
.Wrap = wdFindStop
.Style = "prod_code"
.Execute
End With
Do While .Find.Found
If .Information(wdWithInTable) = True Then
MsgBox .Text
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
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub

VBA and MSWord: Use multiple values of a find parameter in Find/Execute routine

I have a Find/Execute routine that looks for paragraphs in my custom style, Bullet_Type_1_Level_1, which is a custom bulleted list style, and processes the paragraphs. (It checks each paragraph in the given range to see if it terminates in a period or not, but that's not important for this question). The routine currently works fine, but I want to expand it to search for additional levels--which translates into additional styles--of my outline list and to search for a style in another list, too. Is there a compact way to have my code also look for paragraphs in Bullet_Type_1_Level_2 and numlist_Level_1 (and process them, too) while it's at it? Here's the guts of my existing code:
For Each para In RangeToCheck.Paragraphs
With Selection.Find
.Text = ""
.Style = "Bullet_Type_1_Level_1"
.Wrap = wdFindStop
.Execute
Do While .Found = True 'Look for the specified style
strSentence = Selection.Text
'Test the string using a block of code that I'm omitting, for brevity.
'Finally, depending on what happened, put or don't a period at the end of the original range.
End With
Next para
You can add another loop.
Declare i (or more meaningful variable name), and loop through that.
Dim i As Long
For Each para In RangeToCheck.Paragraphs
For i = 1 To 3
With Selection.Find
.Text = ""
Select Case i
Case 1
.Style = "Bullet_Type_1_Level_1"
Case 2
.Style = "Bullet_Type_1_Level_2"
Case 3
.Style = "numlist_Level_1"
End Select
.Wrap = wdFindStop
.Execute
Do While .Found = True 'Look for the specified style
strSentence = Selection.Text
'Test the string using a block of code that I'm omitting, for brevity.
'Finally, depending on what happened, put or don't a period at the end of the original range.
End With
Next i
Next para
Probably not the prettiest solution out there - word is not my strong point ☺.
An alternative approach that may be quicker if there are paragraphs that are none of those Styles:
Dim i As Long
For i = 1 To 3
With RangeToCheck
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Forward = True
.Format = True
.Wrap = wdFindStop
.Style = "Bullet_Type_1_Level_" & i
.Execute
End With
Do While .Find.Found = True
If .InRange(RangeToCheck) = False Then Exit Do
Select Case i
Case 1 'Do something for Bullet_Type_1_Level_1
Case 2 'Do something for Bullet_Type_1_Level_2
Case 3 'Do something for Bullet_Type_1_Level_3
End Select
If ActiveDocument.Range.End = RangeToCheck.Range.End Then Exit Do
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Next

Word VBA highlighting text

I'm generating some security report in Microsoft Word - importing SOAP xml requests and responses...
I want to automate this process as much as I can and I need to highlight some text in these requests/responses. How to do that? In general I need to highlight non-standart inputs in requests (every time different - bad data types and so on) and fault strings in responses (in majority looks like this <faultstring>some error</faultstring>).
Heres code Im trying:
Sub BoldBetweenQuotes()
' base for a quotes finding macro
Dim blnSearchAgain As Boolean
' move to start of doc
Selection.HomeKey Unit:=wdStory
' start of loop
Do
' set up find of first of quote pair
With Selection.Find
.ClearFormatting
.Text = "<faultstring>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Execute
End With
If Selection.Find.Found Then
Selection.MoveRight Unit:=wdCharacter, Count:=1
' switch on selection extend mode
Selection.Extend
' find second quote of this pair
Selection.Find.Text = "</faultstring>"
Selection.Find.Execute
If Selection.Find.Found Then
Selection.MoveLeft Unit:=wdCharacter, Count:=Len(Selection.Find.Text)
' make it bold
Selection.Font.Bold = True
Selection.Collapse Direction:=wdCollapseEnd
Selection.MoveRight Unit:=wdCharacter, Count:=1
blnSearchAgain = True
Else
blnSearchAgain = False
End If
Else
blnSearchAgain = False
End If
Loop While blnSearchAgain = True
End Sub
It highlights just the first faultstring, but other appearences stay unformated nad I dont know why.... Thanks for your reply.
The most efficient way to do this is to work with multiple Range objects. Think of a Range as being like an invisible selection, with the important difference that, while there can be but one Selection object there can be multiple Range objects in your code.
I've adapted your code, adding three Range objects: one for the entire document; one for finding the starting tag; one for finding the end tag. The Duplicate property is used to "copy" one Range from another (this due to an oddity in Word if you Set one Range to another, which links them).
For clarity I also added a couple more Boolean test values for your Ifcomparisons. In my experience, it's more reliable to pick up the "success" directly from Execute than to rely on Find.Found after-the-fact.
Sub BoldBetweenQuotes()
' base for a quotes finding macro
Dim blnSearchAgain As Boolean
Dim blnFindStart As Boolean
Dim blnFindEnd As Boolean
Dim rngFind As word.Range
Dim rngFindStart As word.Range
Dim rngFindEnd As word.Range
Set rngFind = ActiveDocument.content
Set rngFindStart = rngFind.Duplicate
Do
' set up find of first of quote pair
With rngFindStart.Find
.ClearFormatting
.Text = "<faultstring>"
.Replacement.Text = ""
.Forward = True
.wrap = wdFindStop
blnFindStart = .Execute
End With
If blnFindStart Then
rngFindStart.Collapse wdCollapseEnd
Set rngFindEnd = rngFindStart.Duplicate
rngFindEnd.Find.Text = "</faultstring>"
blnFindEnd = rngFindEnd.Find.Execute
If blnFindEnd Then
rngFindStart.End = rngFindEnd.Start
' make it bold
rngFindStart.Font.Bold = True
rngFindStart.Start = rngFindEnd.End
rngFindStart.End = rngFind.End
blnSearchAgain = True
Else
blnSearchAgain = False
End If
Else
blnSearchAgain = False
End If
Loop While blnSearchAgain = True
End Sub

Remove all text between 2 headers Word 2010, using VBA

I have 2 headers or markers that are a part of my RTF document. In my example I am showing a sentence when in reality it will be multiple sentences or paragraphs. I have used brackets instead of less than and greater than signs as they disappear in my question. All I want to do is replace the text between the 2 markers with the following sentence, "text goes here", without quotation marks.
[EmbeddedReport]Lots of text, thousands of character, multiple paragraphs[/EmbeddedReport]
I want replace all the text between the 2 markers replaced with "text goes here".
It would end up looking like this...
"[EmbeddedReport]text goes here[/EmbeddedReport]"
I've literally spent 2 days trying to solve this. Any help would be appreciated.
This is the last thing I tried...
Sub RemoveReport()
Dim c As Range
Dim StartWord As String, EndWord As String
Selection.HomeKey Unit:=wdStory
StartWord = "<ImageTable>"
EndWord = "</ImageTable>"
Set c = ActiveDocument.Content
c.Find.ClearFormatting
c.Find.Replacement.ClearFormatting
With c.Find
.Text = StartWord & "*" & EndWord
' MsgBox (.Text)
.Replacement.Text = "<ImageTable>text goes here</ImageTable>"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
c.Find.Execute
While c.Find.Found
Debug.Print c.Text
'~~> I am assuming that the start word and the end word will only
'~~> be in the start and end respectively and not in the middle
Debug.Print Replace(Replace(c.Text, StartWord, ""), EndWord, "")
c.Find.Execute
Wend
End Sub
Word VBA is not my area of expertise, but it seems similar to a question I answered a few days ago.
Turns out the wildcard match was not doing what I hoped it would do, or at least it was not reliable. Also, I ran in to some trouble using angle brackets, so this uses square brackets. I suspect that word treats the angle brackets as markup/syntax, and thus does not interpret them as text in the Find object. There is probably a way around this, but Word VBA is not my specialty. There is also probably a more elegant solution, but again, Word VBA is not my specialty :)
Try something like this:
Option Explicit
Sub Test()
Dim doc As Document
Dim txtRange As Range
Dim startTag As String
Dim endTag As String
Dim s As Long
Dim e As Long
startTag = "[EmbeddedReport]"
endTag = "[/EmbeddedReport]"
Set doc = ActiveDocument
Set txtRange = doc.Content
'Find the opening tag
With txtRange.Find
.Text = startTag
.Forward = True
.Execute
If .Found Then
s = txtRange.Start
Else
GoTo EarlyExit
End If
End With
'Find the closing tag
Set txtRange = doc.Range(txtRange.End, doc.Content.End)
With txtRange.Find
.Text = endTag
.Forward = True
.Execute
If .Found Then
e = txtRange.End
Else
GoTo EarlyExit
End If
End With
Set txtRange = doc.Range(s, e)
txtRange.Text = startTag & "text goes here" & endTag
Exit Sub
EarlyExit:
MsgBox "Header not found in this document!", vbInformation
End Sub
It takes some time to figure it out at first, but learning to navigate the object model reference documentation for VBA will make these tasks a lot easier to figure out in the future.