Use Word Macro to Determine last character of Paragraph - vba

I have been using this code to Bold-Underline all the headers in my word doc:
Sub Underline_Headers()
Dim p As Paragraph
For Each p In ActiveDocument.Paragraphs
If Len(p.Range.Text) < 70 Then
p.Range.Font.Underline = True
p.Range.Font.Bold = True
End If
Next p
End Sub
This works great - as long as every header is less than 70 characters long, and the paragraph underneath it is 70 or more characters.
But many times the header can be longer than 70 characters, and the paragraph under the header can be less than 70 characters.
However, the headers always never end with any punctuation, like a "." but the paragraphs underneath them always do.
I am trying to fix the code above to look for all paragraphs not ending in a "." and then Bold-Underline them. In other words, I want to change the rule.
I tried the only thing that made sense to me. The code did not break, but it ended up bold-underline the entire document:
Sub Underline_Headers()
Dim p As Paragraph
For Each p In ActiveDocument.Paragraphs
If Right(p.Range.Text,1) <> "." Then
p.Range.Font.Underline = True
p.Range.Font.Bold = True
End If
Next p
End Sub
This supposedly looks for all paragraphs where the last character is not ".", which if that worked, would isolate all the headers and only bold-underline them, but obviously that doesn't work.

The last character in every paragraph is a carriage return, Chr(13). The text ends one character before that. The code below also considers the possibility that someone ended a paragraph's text with one or more blank spaces. It takes the "cleaned" string and looks for the last character in a string of possible exceptions, like .?!. You can reduce this string to a single full stop or extend it to include more cnadidates for exception.
Private Sub UnderlineTitles()
Dim Para As Paragraph
Dim Txt As String
Application.ScreenUpdating = False
For Each Para In ActiveDocument.Paragraphs
Txt = Para.Range.Text
Txt = RTrim(Left(Txt, Len(Txt) - 1))
' you can extend the list to include characters like ")]}"
If InStr(".?!", Right(Txt, 1)) = 0 Then
' to choose a different style of underline, remove
' "= wdUnderlineSingle", type "=" and select from the dropdown
Para.Range.Font.Underline = wdUnderlineSingle
End If
Next Para
Application.ScreenUpdating = True
End Sub

Related

Set specific font size for Hebrew characters in Word in VBA

I am in need to modify this code which is made for Word 2007 to Word XP. I need to set size for hebrew text in document. It is mix of Central Europe words combined with hebrew. What I need is hebrew text in bigger size. This contains only basic hebrew characters, no vowels. The non hebrews characters can be English or Czech (http://www.biega.com/special-char.html), the hebrew Chars are from 1488 to 1514.
But I could not run the code send here Flag special characters in document using VBA in Word in the answer as I got error (Range?) object not selected.
Sub test()
'Options.DefaultHighlightColorIndex = wdNoHighlight
'Range.HighlightColorIndex = wdNoHighlight ' used for testing to clear Highlight
Dim r As Range, t As Double: t = Timer
Application.ScreenUpdating = False
For Each r In Range.Characters ' For Each r In Range.Words is somehow about 2 times slower than .Characters
checkRange r
Next
Application.ScreenUpdating = True
Debug.Print Timer - t; Range.Words.Count; Range.Characters.Count; Range.End ' " 3.15625 8801 20601 20601 "
End Sub
Sub checkRange(r As Range)
Dim b() As Byte, i As Long, a As Long
b = r.Text ' converts the string to byte array (2 or 4 bytes per character)
'Debug.Print "'" & r & "'"; r.LanguageID; r.LanguageIDFarEast; r.LanguageIDOther
For i = 1 To UBound(b) Step 2 ' 2 bytes per Unicode codepoint
If b(i) > 0 Then ' if AscW > 255
a = b(i): a = a * 256 + b(i - 1) ' AscW
Select Case a
Case &H1F00 To &H1FFF: r.HighlightColorIndex = wdBlue: Exit Sub ' Greek Extended
Case &H3040 To &H30FF: r.HighlightColorIndex = wdPink: Exit Sub ' Hiragana and Katakana
Case &H4E00 To 40959: r.HighlightColorIndex = wdGreen: Exit Sub ' CJK Unified Ideographs
Case 55296 To 56319: ' ignore leading High Surrogates ?
Case 56320 To 57343: ' ignore trailing Low Surrogates ?
Case Else: r.HighlightColorIndex = wdRed: Debug.Print Hex(a), r.End - r.Start ' other
End Select
End If
Next
End Sub
So I only need to select the size and make this working for Word XP. Any help?
Notice:
Whole document and the hebrew chars are written from left to right (when I typed hebrew in online keyboard, they were RTL, but after coping to Word they were LTR. But this was not problem, because I have copied/pasted them to display the hebrew word correctly. So they are in LTR in fact.
The error is because you haven't specified which object's range you want to process.
For Each r In Range.Characters
Needs to be qualified either as
For Each r In Selection.Range.Characters
or as
For Each r In ActiveDocument.Range.Characters
You could have answered this for yourself simply by looking up the online help for Range
NOTE:
Processing a document character by character is going to be a very long process. If the characters you want to process can be identified by language id you can speed up the process considerably.

Word VBA uppercase first character of words issues

Ok, lets say i want to uppercase first character in words in selected range in Word document, except some expressions. I have a script which does similar thing, but i have issues with some expressions. In script i use Selection.Range.Case, but the problem is with URL addresses. Id like to leave everything in addresses smallcased, but Selection.Range.Case dissolves URL links to multiple strings, like: https, :, //, /, etc. end every first character of that URL string gets uppercased. The selected range of text is in numbered list, and URLs are last thing before next numbered item. Is there some solution where i can concatenate everything after http:// or https:// into one string right before next numbered item ? Thank you.
May try something like this if the selected range of text is only in numbered list
Sub test()
Dim Pg As Paragraph, Pos As Long, Rng As Range
For Each Pg In Selection.Paragraphs
If Not Pg.Range.ListFormat.List Is Nothing Then 'Process only bulleted list
PgTxt = Pg.Range.Text
'Debug.Print PgTxt
Pos = InStr(1, PgTxt, "http")
If Pos <> 1 Then 'bypass if http found at start of List item then no Case Change
If Pos > 1 Then Pos = Pos - 1 'http found some where within the List item
If Pos = 0 Then Pos = Len(PgTxt) ' if http not found in the list Item
Set Rng = ActiveDocument.Range(Pg.Range.Start, Pg.Range.Start + Pos)
Rng.Case = wdTitleWord
End If
End If
Next
End Sub

Excel cell content validation with use of VBA code

I am looking for a solution to validate and highlight my cell in case false.
I tried the most promising solution: Regex. But still can not find the pattern I need.
My latest attempt was this pattern: "[A-Z-0-9_.]" This works only if the cell contains only a symbol and nothing else, if the symbol is part of a string it does not work.
Problem is that it does not catch cells that have an odd character in a string of text: Example C4UNIT| or B$GROUP.
Specification Cell can contain only capital characters and two allowed symbols Dash - and Underbar _
This is my complete code:
Function ValidateCellContent()
Sheets("MTO DATA").Select
Dim RangeToCheck As Range
Dim CellinRangeToCheck As Range
Dim CollNumberFirst As Integer
Dim CollNumberLast As Integer
Dim RowNumberFirst As Integer
Dim RowNumberLast As Integer
'--Start on Column "1" and Row "3"
CollNumberFirst = 1
RowNumberFirst = 3
'--Find last Column used on row "2" (Write OMI Headings)
CollNumberLast = Cells(2, Columns.count).End(xlToLeft).Column
RowNumberLast = Cells(Rows.count, 1).End(xlUp).Row
'--Set value of the used range of cell addresses like: "A3:K85"
Set RangeToCheck = Range(Chr(64 + CollNumberFirst) & RowNumberFirst & ":" & Chr(64 + CollNumberLast) & RowNumberLast)
Debug.Print "Cells used in active Range = " & (Chr(64 + CollNumberFirst) & RowNumberFirst & ":" & Chr(64 + CollNumberLast) & RowNumberLast)
For Each CellinRangeToCheck In RangeToCheck
Debug.Print "CellinRangeToCheck value = " & CellinRangeToCheck
If Len(CellinRangeToCheck.Text) > 0 Then
'--Non Printables (Space,Line Feed,Carriage Return)
If InStr(CellinRangeToCheck, " ") _
Or InStr(CellinRangeToCheck, Chr(10)) > 0 _
Or InStr(CellinRangeToCheck, Chr(13)) > 0 Then
CellinRangeToCheck.Font.Color = vbRed
CellinRangeToCheck.Font.Bold = True
'--Allowed Characters
ElseIf Not CellinRangeToCheck.Text Like "*[A-Z-0-9_.]*" Then
CellinRangeToCheck.Font.Color = vbRed
CellinRangeToCheck.Font.Bold = True
Else
CellinRangeToCheck.Font.Color = vbBlack
CellinRangeToCheck.Font.Bold = False
End If
End If
Next CellinRangeToCheck
End Function
Try this:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
'we want only validate when cell content changed, if whole range is involved (i.e. more than 1 cell) then exit sub
If Target.Cells.Count > 1 Then Exit Sub
'if there is error in a cell, also color it red
If IsError(Target) Then
Target.Interior.ColorIndex = 3
Exit Sub
End If
'validate cell with our function, if cell content is valid, it'll return True
'if it i s not valid, then color cell red
If Not ValidateText(Target.Value) Then
Target.Interior.ColorIndex = 3
End If
End Sub
Function ValidateText(ByVal txt As String) As Boolean
Dim i As Long, char As String
'loop through all characters in string
For i = 1 To Len(txt)
char = Mid(txt, i, 1)
If Not ((Asc(char) >= 65 And Asc(char) <= 90) Or char = "-" Or char = "_") Then
'once we come upon invalid character, we can finish the function with False result
ValidateText = False
Exit Function
End If
Next
ValidateText = True
End Function
I've originally assumed you wanted to use RegEx to solve your problem. As per your comment you instead seem to be using the Like operator.
Like operator
While Like accepts character ranges that may resemble regular expressions, there are many differences and few similarities between the two:
Like uses ! to negate a character range instead of the ^ used in RegEx.
Like does not allow/know quantifiers after the closing bracket ] and thus always matches a single character per pair of brackets []. To match multiple characters you need to add multiple copies of your character range brackets.
Like does not understand advanced concepts like capturing groups or lookahead / lookbehind
probably more differences...
The unavailability of quantifiers leaves Like in a really bad spot for your problem. You always need to have one character range to compare to for each character in your cell's text. As such the only way I can see to make use of the Like operator would be as follows:
Private Function IsTextValid(ByVal stringToValidate As String) As Boolean
Dim CharValidationPattern As String
CharValidationPattern = "[A-Z0-9._-]"
Dim StringValidationPattern As String
StringValidationPattern = RepeatString(CharValidationPattern, Len(stringToValidate))
IsTextValid = stringToValidate Like StringValidationPattern
End Function
Private Function RepeatString(ByVal stringToRepeat As String, ByVal repetitions As Long) As String
Dim Result As String
Dim i As Long
For i = 1 To repetitions
Result = Result & stringToRepeat
Next i
RepeatString = Result
End Function
You can then pass the text you want to check to IsTextValid like that:
If IsTextValid("A.ASDZ-054_93") Then Debug.Print "Hurray, it's valid!"
As per your comment, a small Worksheet_Change event to place into the worksheet module of your respective worksheet. (You will also need to place the above two functions there. Alternatively you can make them public and place them in a standard module.):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ValidationRange As Range
Set ValidationRange = Me.Range("A2:D5")
Dim TargetCell As Range
For Each TargetCell In Target.Cells
' Only work on cells falling into the ValidationRange
If Not Intersect(TargetCell, ValidationRange) Is Nothing Then
If IsTextValid(TargetCell.Text) Then
TargetCell.Font.Color = vbBlack
TargetCell.Font.Bold = False
Else
TargetCell.Font.Color = vbRed
TargetCell.Font.Bold = True
End If
End If
Next TargetCell
End Sub
Regular Expressions
If you want to continue down the RegEx road, try this expression:
[^A-Z0-9_-]+
It will generate a match, whenever a passed-in string contains one or more characters you don't want. All cells with only valid characters should not return a match.
Explanation:
A-Z will match all capital letters,
0-9 will match all numbers,
_- will match underscore and dash symbols.
The preceding ^ will negate the whole character set, meaning the RegEx only matches characters not in the set.
The following + tells the RegEx engine to match one or more characters of the aforementioned set. You only want to match your input, if there is at least one illegal char in there. And if there are more than one, it should still match.
Once in place, adapting the system to changing requirements (different chars considered legal) is as easy as switching out a few characters between the [brackets].
See a live example online.

VBA Find function incorrect highlighting

Can somebody help me fix this code, I have two textbox where when you paste the text on the 1st Textbox and click the search button, it highlighted the exact text on the 2nd textbox if it is present on the 2nd textbox. But when the string on 2nd textbox contains linefeed/newline, it added one character from the start of the text. Here is the code:
Private Sub FindText(ByVal start_at As Integer)
Dim pos As Integer
Dim target As String
target = Textbox1.Text
pos = InStr(start_at, Textbox2.Text, target)
If pos > 0 Then
' We found it.
TargetPosition = pos
Textbox2.SelStart = TargetPosition - 1
Textbox2.SelLength = Len(target)
Textbox2.SetFocus
Else
' We did not find it.
MsgBox "Not found."
Textbox2.SetFocus
End If
End Sub
' Search button
Private Sub cmdSearch_Click()
FindText 1
End Sub
I believe the problem is that Textbox is treating newline as a single character while Len() counts as CRLF as two. You should probably count the number of times CRLF appears in the text preceding the match target and adjust SelStart accordingly. Try this line instead:
Textbox2.SelStart = Len(Replace(Left(target, pos - 1), vbCrLf, "^")) - 1
'Textbox2.SelLength = Len(Replace(target, vbCrLf, "^"))
If target can include line breaks you may have a similar problem with SelLength which is why I've left the second, commented line. This works by substituting two-character line-break sequences into a single-character string. It's completely arbitrary what value to use for replacement since the result is discarded and only the length ultimately matters.

How to extract specific text from a cell?

In this case, I want to extract the beginning text in a cell and leave the remainder intact.
e.g. a series of cells contain:
2nd Unit. Miami
3rd Production Staff. Toronto
1st Ad. San Francisco
I want to break this up without using Text to columns as previous rows are formatted differently and these last few rows are outliers that I want to handle.
I thought Regular Expressions might do it, but that seems a bit complex.
My algorithm idea is:
1. grab the wanted text (what function or custom sub would do that?)
2. Past the text to it's new location
3. Cut the text from the cell, leaving the remaining text.
Seems simple but I'm still wending my way through VBA forest, and at the rate I'm going it's going to end up faster doing it by hand. But this seems like a good opportunity to learn some VBA tricks.
TIA
Update:
I want to take the text up to the ".\ " and move it to a different column, keeping the remainder where it is.
VBA is unnecessary. To get the text after .\ in cell A1: =MID(A1,FIND(".\",A1,1)+2,LEN(A1)) to get the text before .\ in A1: =LEFT(A1,FIND(".\",A1,1)-1).
As additional information, Find returns the placement in the string where .\ appears. It is the equivalent of InStr in VBA. If .\ is not in the cell, it will display #VALUE, because I didn't bother to add error checking.
Since you seem to want to modify the cell text in place, VBA will be required.
Inside a loop that sets cl to the cell to be processed:
str = cl.value
i = Instr(str, ".\")
cl = Trim(Mid$(str, i + 2)) ' assuming you want to exclude the ".\"
cl.Offset(0, 1) Trim(Left$(str, i - 1)) ' Places the original first part one cell to the right
For the sake of anyone who had this same question, here is the fully tested, working code.
Function RE6(strData As String) As String
Dim RE As Object, REMatches As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.MultiLine = False
.Global = False
.IgnoreCase = True
.Pattern = "[0-9][0-9][0-9][0-9]B"
RE6 = .test(strData)
End With
Set REMatches = RE.Execute(strData)
If REMatches.Count > 0 Then
RE6 = True
Else
RE6 = False
End If
End Function
Sub territory()
Dim strTest As String, str As String, cl As Range
strTest = ActiveCell.Value
Set cl = ActiveCell
If RE6(strTest) = True Then
str = cl.Value
i = InStr(str, ". ")
cl = Trim(Mid$(str, i + 2))
cl.Offset(0, 1) = Trim(Left(str, i - 1))
cl.Offset(0, 2) = "Instance"
MsgBox RE6(strTest)
End If
End Sub