Excel Macro VBA Use HTML Tags Bold Italics Underline Strong in Cell - html

I have been looking to convert a string or cell such as:
[Cell B2 Example]
"This is a <b>test</b> cell <i>filled</i> with <strong>randomly placed html tags</strong>."
[Needed Output Example] "This is a test cell filled with randomly placed html tags."
I need to be able to process multiple types of tags (<b></b> , <i></i> , <u></u> , <strong></strong>) in the same cell or string.
So far, someone has helped me with getting this far:
Dim Tag, Tend, Pstart, Pend As String
'BOLD Text
Tag = "<b>" ' tag string: start
Tend = "</b>" ' tag string: end
Pstart = 0 ' vector index of Pos()
Pend = 1 ' vector index of Pos()
Dim Cv As String ' Cell value
Dim Cnt As Integer ' instances of bold expressions
Dim Pos() As Variant ' string positions: 0 = start, 1 = End
Dim f As Integer ' loop counter: Cnt
Cv = Range("B2").Value
Cnt = (Len(Cv) - Len(Replace(Cv, Tag, ""))) / 3
ReDim Pos(Cnt, Pend)
For f = 1 To Cnt
Pos(f, Pstart) = InStr(Cv, Tag)
Cv = Left(Cv, Pos(f, Pstart) - 1) & Mid(Cv, Pos(f, Pstart) + Len(Tag), Len(Cv))
Pos(f, Pend) = InStr(Cv, Tend) - 1
Cv = Left(Cv, Pos(f, Pend)) & Mid(Cv, Pos(f, Pend) + Len(Tend) + 1, Len(Cv))
Next f
With Range("B2")
.Font.Bold = False
.Value = Cv
For f = 1 To Cnt
.Characters(Pos(f, Pstart), Pos(f, Pend) - Pos(f, Pstart) + 1).Font.Bold = True
Next f
End With
The above successfully makes needed text bold AND removes the visual tags from the cell.
However, when trying to also incorporate italics, underline, and strong tags, it only does which ever come last. The rest get wiped out.
Is there a better way to do this?
Can multiple html tags be converted in excel strings or cells WITHOUT having to open other applications such as IE, etc?
Side note, as for the tags, it would be fine if they functioned the same as bold, if that makes it easier?

As soon as you assign the cell's .Value property, any per-character font formatting will be lost, so you can't do that as part of the formatting process.
Here's one way to do it - not bulletproof and will not account for (eg) nested sets of the same tag or invalid HTML...
Sub Tester()
Dim c As Range
Set c = ActiveSheet.Range("D5")
ActiveSheet.Range("D2").Copy c 'for testing:copy the input string
FormatTags c, "b", "bold"
FormatTags c, "i", "italic"
FormatTags c, "strong", "bold"
FormatTags c, "u", "underline"
End Sub
Sub FormatTags(c As Range, tag As String, prop As String)
Dim pOpen As Long, pClose As Long, numChars As Long
Dim sOpen, sClose
sOpen = "<" & tag & ">" 'the open tag
sClose = "</" & tag & ">" 'close tag
pOpen = InStr(c.Value, sOpen) 'have an open tag?
Do While pOpen > 0
pClose = InStr(pOpen + 1, c.Value, sClose) 'find next close tag
If pClose > 0 Then
c.Characters(pClose, Len(sClose)).Delete 'remove the close tag first
c.Characters(pOpen, Len(sOpen)).Delete 'remove the open tag
'set the named font property
numChars = pClose - (pOpen + Len(sOpen))
CallByName c.Characters(pOpen, numChars).Font, prop, VbLet, True
pOpen = InStr(c.Value, sOpen) 'find next, if any
Else
Exit Do 'no closing tag - all done
End If
Loop
End Sub
Edit - if you're interested in a more general-purpose approach which doesn't involve IE you can copy the HTML to the clipboard and paste it to a cell. That will give you the formatting you want.
Eg - using #GMCB's code from here: Injecting RTF code in the Clipboard to paste into MS Word as RTF text via a VBA macro
With ActiveSheet
myClipboard.SetClipboardText .Range("D5").value, "HTML Format"
.Paste Destination:=.Range("D5")
End With

Related

Excel VBA Macro Replace Html Bold Tag With Bolded Text In Cell

I have the following:
s = 1
f = 1
For i = 1 To UBound(Split(Range("B17").Value, "<b>"))
s = InStr(f, Range("B17").Value, ("<b>"))
f = InStr(s, Range("B17").Value, ("</b>"))
Range("B17").Characters(s, f - s + 1).Font.FontStyle = "Bold"
Next i
This works to loop a cell and make all text between tags bolded.
However, this also still leaves behind the tags in the cell.
I need a way to bold between AND remove the tags from a specific cell.
I tried to add:
Range("B17").Value = Replace(Range("B17").Value, "<b>", "")
Range("B17").Value = Replace(Range("B17").Value, "</b>", "")
BUT, this not only removed the tags, it also removed the bold font.
Is it possible to do this?
This code first notes the position of the tags before removing them. Then, in a separate loop, it applies bold font to the noted text positions.
Private Sub SetCharsBold(Cell As Range)
' 086
Const Tag As String = "<b>" ' tag string: start
Const Tend As String = "</b>" ' tag string: end
Const Pstart As Integer = 0 ' vector index of Pos()
Const Pend As Integer = 1 ' vector index of Pos()
Dim Cv As String ' Cell value
Dim Cnt As Integer ' instances of bold expressions
Dim Pos() As Variant ' string positions: 0 = start, 1 = End
Dim f As Integer ' loop counter: Cnt
Cv = Cell.Value
Cnt = (Len(Cv) - Len(Replace(Cv, Tag, ""))) / 3
ReDim Pos(Cnt, Pend)
For f = 1 To Cnt
Pos(f, Pstart) = InStr(Cv, Tag)
Cv = Left(Cv, Pos(f, Pstart) - 1) & Mid(Cv, Pos(f, Pstart) + Len(Tag), Len(Cv))
Pos(f, Pend) = InStr(Cv, Tend) - 1
Cv = Left(Cv, Pos(f, Pend)) & Mid(Cv, Pos(f, Pend) + Len(Tend) + 1, Len(Cv))
Next f
With Cell.Offset(18)
.Font.Bold = False
.Value = Cv
For f = 1 To Cnt
.Characters(Pos(f, Pstart), Pos(f, Pend) - Pos(f, Pstart) + 1).Font.Bold = True
Next f
End With
End Sub
I thought it's a bit slow. Therefore I wanted to pause screen updating (Application.ScreenUpdating = False) while it runs but refrained. The reason is that the procedure just formats a single cell. You would probably call it from another procedure that loops through all your cells in a column, feeding each one to the above proc in turn. Use code like SetCharsBold Range("F1"). The screen control should be done in that procedure, delaying the update until its loop has run.
I forgot to remove Cell.Offset(18) from the code and decided to leave it there on second thought. I didn't want the code to over-write the original texts. Perhaps you have a similar need. Please adjust that line to suit.

How to get text between div without any tag? <div class="obj-amount">15</div>

I have this code in VBA and I want to extract '15' of obj-amount div class. Any suggestion?
<div class="obj-amount">15<span class="unit">$</span></div>
Set divtags = oHtml.getElementsByClassName("obj-amount")(0).getElementsByTagName("obj-amount")
i = 0
For Each oElement In divtags
Sheets("Data").Range("A" & i + 1) = divtags(i).innerText
i = i + 1
Next oElement
Returning a collection and looping:
You don't want to chain together ByClassName and ByTagName. Your selector inside is for classname alone and is sufficient to return a collection of elements with that classname.
You don't want to index at that level either, if after all elements with this class name for a loop. You want to For Each over the collection. Then in the loop you want to work with the loop variable, oElement; that'll mean you can start i=1 and reduce the amount of code and calls for addition in the loop.
This will of course return the $ which resides in the child span tag.
Ways to avoid/remove the $ (or child span content):
The simplest way to remove this, if unwanted, is to use Replace$ on the .innerText during the loop.
If that text is not constant then you can do a replacement of oElement.children(0).innerText with vbNullString on the .innerText, or of oElement.getElementsByTagName("span")(0).innertext. The latter I think might have been what you were after doing (but it needed to be in the loop.).
You could also have done oElement.FirstChild.NodeValue without a replacement.
N.B.
Which methods are available to you will depend on how you declared divtags and oElement.
Long text version:
Set divtags = ohtml.getElementsByClassName("obj-amount") '<== collection matched by classname
i = 1
For Each oElement In divtags '<== loop each item in collection
Worksheets("Data").Range("A" & i) = oElement.innerText
'Worksheets("Data").Range("A" & i) = oElement.FirstChild.NodeValue
'Worksheets("Data").Range("A" & i) = Replace$(oElement.innerText,"$", vbNullString) '<==replacement if wanted
'Worksheets("Data").Range("A" & i) = Replace$(oElement.innerText,oElement.children(0).innerText, vbNullString) '<==replacement if wanted and first child text not constant
'Worksheets("Data").Range("A" & i) = Replace$(oElement.innerText,oElement.getElementsByTagName("span")(0).innertext, vbNullString) '<==replacement if wanted and child span text not constant
i = i + 1
Next oElement
Using With statement and Worksheet variable for legibility:
I would probably put the worksheet into a variable and ensure I am working with Worksheets collection. I would also use With statement to hold reference to oElement inside the loop, so as to use dot accessor, . , for legibility:
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Data")
Set divtags = ohtml.getElementsByClassName("obj-amount")
i = 1
For Each oElement In divtags
With oElement
ws.Range("A" & i) = .FirstChild.NodeValue
ws.Range("A" & i) = Replace$(.innerText, "$", vbNullString) 'next two lines are alternativeS based on need
ws.Range("A" & i) = Replace$(.innerText, .Children(0).innerText, vbNullString)
ws.Range("A" & i) = Replace$(.innerText, .getElementsByTagName("span")(0).innerText, vbNullString)
End With
i = i + 1
Next oElement

Hyphenation in SSRS

I want to add hyphenation to the column headers of a tablix
Consider the column value "waterbodiesinhereforme"
Currently SSRS is hyphenating based on the size it can fit inside the tablix column header. Like below .
waterbodiesinhereforme
But my requirement is
waterbodiesin-
hereforme
So far I have tried the soft hyphen character , ­ which did not work in the ssrs even though html rendering was set to true. Even the Unicode "00AD" did not work.
When I tried with the ZeroWidthCharacter it worked correctly, but I do not know how to introduce a hyphen when there is a new line.
Zero Width Character Example
="water" + ChrW(&h200B) + "bodies" + ChrW(&h200B) + "in" + ChrW(&h200B) + "here" + ChrW(&h200B) + "for" + ChrW(&h200B) + "me"
Things I cannot do
- Hardcode the hyphen (not acceptable because this value is dynamic)
I've written this in Excel VBA, but this can be easily transferred to SSRS.
This splits the input string into parcels of 10 characters separated by carriage returns. You can change the string length by changing the initial value of IntSplit. You could add your zerowidthcharacter if you wanted. The Function code would need to be added to the "Report Properties>Code" section of the SSRS, with the string requiring the split being placed in the Expression for the field:
=code.SplitString(Fields!YourFieldName.value)
Here's the code ...
Private Sub do_it()
Dim strString As String
Dim StrNewString As String
strString = "This is a very long sentence that needs to be chunked up"
strString = SplitString(strString)
Debug.Print strString
End Sub
Private Function SplitString(ByVal strInput As String) As String
Dim StrOut As String
Dim IntSplit As Integer
Dim Intstart As Integer
Dim j As Integer
IntSplit = 10
Intstart = 1
StrOut = ""
For j = 1 To Len(strInput)
If Int(j / IntSplit) = j / IntSplit Then
StrOut = StrOut + Mid(strInput, Intstart, IntSplit) + vbCrLf
Intstart = j + 1
End If
Next
StrOut = StrOut + Mid(strInput, Intstart, Len(strInput) - (Intstart - 1))
SplitString = StrOut
'Return SplitString ' A Return statement is required in SSRS
End Function
Output
This is a
very long
sentence t
hat needs
to be chun
ked up

Export formatted bullets to an email from Excel TextBox

I've been working on a spread sheet to allow my team to manage our workload more effectively, whilst the business is developing a new tool. Anyway, what the sheet does is inject information, then at the click of a button, it populates an OFT email template so that the info can be sent out.
Problem is, we rely heavily on bullet lists for our emails, and I'm really struggling to find a way of adding bullets effectively from an ActiveX Textbox.
At the moment, I have a button which adds the follow to a text box:
[bullets]
* Bullet 1
* Bullet 2
* Bullet 3
[/bullets]
I then have Replace statements that look for strings and it replaces them with the appropriate HTML tags. Here's the code:
' Add HTML formatting to text updates so it displays correctly in the email.
LatestUpdate.Text = Replace(LatestUpdate, "[bullets]", "<ul>")
LatestUpdate.Text = Replace(LatestUpdate, "[/bullets]", "</ul>")
LatestUpdate.Text = Replace(LatestUpdate, "* ", "<li>")
LatestUpdate.Text = Replace(LatestUpdate, vbCrLf, "<br>")
The problem I'm having, is that non-technical people are using this document, so I would really like to have it in such a way were they don't have to look at the markup, but can simple add bullets straight from the textbox.
I was originally thinking about replacing "* " with "< li >" however, that doesn't add the correct < ul > tags, so it's not actually a bullet list within the email.
Can anyone help in simplifying this process for the end users please? I'm really stuck.
The holy grail would be to enable rich text formatting on the textbox, but I don't believe that's possible from all the research I've done?
TIA.
Based on your last comment, what you are looking for is not just a bullet point in your textbox but indentation as well. So here is an attempt at it:
First add the below in your <textbox>_KeyUp function:
Private Sub txtBulletPoints_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Dim STRING_LENGTH As Long: STRING_LENGTH = 49
Dim aLine() As String
Dim aLineSpace() As String
Dim iC As Integer
Dim sText As String
Dim bUpdate As Boolean
' Only do this if there is a string to work with
If Len(Me.txtBulletPoints.Text) > 0 Then
' Set initial values
aLine = Split(Me.txtBulletPoints.Text, vbCrLf)
bUpdate = False
' First lets indent the last line if we need to
If Left(aLine(UBound(aLine)), 2) = "- " Then
For iC = LBound(aLine) To UBound(aLine)
If iC = UBound(aLine) Then
sText = sText & vbTab & aLine(iC)
Else
sText = sText & aLine(iC) & vbCrLf
End If
Next
Me.txtBulletPoints.Text = sText
End If
' Now the tricky bit. Check if we have reached the end of the
' line so that we can indent the text into the next line
If (Len(aLine(UBound(aLine))) >= STRING_LENGTH) And (InStr(1, aLine(UBound(aLine)), vbTab) = 1) Then
For iC = LBound(aLine) To UBound(aLine)
If iC = UBound(aLine) Then
aLineSpace = Split(aLine(iC), " ")
' As we have to indent the last bullet point line, call the finction to do that
sText = sText & SetIndentsInString(aLine(iC), STRING_LENGTH)
Else
sText = sText & aLine(iC) & vbCrLf
End If
Next
Me.txtBulletPoints.Text = sText
End If
End If
End Sub
Now add the below UDF where your form code is (essentially at the same place where your <textbox>_KeyUp function is):
Function SetIndentsInString(ByVal sString As String, ByVal iIndentLen As Long) As String
Dim iC As Long
Dim iLastTab As Long: iLastTab = 0
Dim aSpace() As String
Dim aTab() As String
Dim sCurString As String
' Check if the string is the same as what it was last
' time (sLastString is a private module variable initialised
' to "" when the form is activated)
If Replace(sString, vbTab, "") = Replace(sLastString, vbTab, "") Then
' Its the same string so lets return it as is
SetIndentsInString = sString
Else
' Its not the same string so set initial values
sLastString = sString
SetIndentsInString = ""
' Loop to see how many lines we have based on number of TABs in the string
Do While InStr(iLastTab + 1, sString, vbTab) > 0
iLastTab = iLastTab + InStr(iLastTab + 1, sString, vbTab)
Loop
' If there is only 1 TAB, simply indent the line
If iLastTab = 1 Then
aSpace = Split(sString, " ")
SetIndentsInString = Mid(sString, 1, Len(sString) - Len(aSpace(UBound(aSpace)))) & vbTab & " " & aSpace(UBound(aSpace))
Else
' More then 1 TAB.. damn!. Ok well lets work it
aTab = Split(sString, vbTab)
sCurString = aTab(UBound(aTab))
' Check if the last line of our bullet point has more characters then allowed in a line
If Len(sCurString) >= iIndentLen Then
' It does. Now loop through all the lines in our bullet point and set the last character in a new line with indent
aSpace = Split(sCurString, " ")
For iC = LBound(aTab) To UBound(aTab)
If iC = UBound(aTab) Then
SetIndentsInString = SetIndentsInString & Mid(sCurString, 1, Len(sCurString) - Len(aSpace(UBound(aSpace)))) & vbTab & " " & aSpace(UBound(aSpace))
Else
SetIndentsInString = SetIndentsInString & aTab(iC) & vbTab
End If
Next
Else
' It doesnt. Loop through and send the string back
SetIndentsInString = sString
End If
End If
End If
End Function
Now in the same module, make the following declaration at the top:
Private sLastString As String
Essentially the above will act like a bullet point as it would be in a Rich Text box. Things to remember is that you will have to set STRING_LENGTH to the number of characters your textbox will take in a given bullet point line (you will have to play around with that). Below is a screen print of how it worked for me

MS Access report- line break at character?

I have built a report within MS Access which includes a short textbox that contains web addresses. The textbox has the "CanGrow" option set to "Yes".
Because there is limited horizontal space on the report for this field, and web addresses can be very long, rather than just having the web address spill over where ever the text length forces it to such as:
http://stackoverflow.com/que
stions/ask
I am wondering if there is a way to force the text string to word wrap at the last appropriate character, in this case the "/" character. The result would be something that looks more readable such as:
http://stackoverflow.com/
questions/ask
Can this be done? Any suggestions on how to approach this task?
The following recursive function will insert a carriage-return/line-feed based on user-defined characters and a max line length. This will work best with a fixed-width font, but with some experimentation should also be acceptable for a variable width font:
Function PrettyBreak(Txt As String, MaxCharsPerLine As Long, _
Optional BreakAfterChars As String = ":=-+&?./ ") As String
Dim t As String, i As Integer, Pos As Integer
If Len(Txt) > MaxCharsPerLine Then
t = Left(Txt, MaxCharsPerLine)
For i = MaxCharsPerLine To 1 Step -1
If InStr(BreakAfterChars, Mid(t, i, 1)) <> 0 Then
Pos = i
Exit For
End If
Next i
If Pos = 0 Then
PrettyBreak = t & vbCrLf & _
PrettyBreak(Mid(Txt, MaxCharsPerLine + 1), _
MaxCharsPerLine, BreakAfterChars)
Else
PrettyBreak = Left(t, Pos) & vbCrLf & _
PrettyBreak(Mid(Txt, Pos + 1), _
MaxCharsPerLine, BreakAfterChars)
End If
Else
PrettyBreak = Txt
End If
End Function
In use:
?prettybreak("http://stackoverflow.com/questions/5583986/ms-access-report-line-break-at-character", 30)
http://stackoverflow.com/
questions/5583986/ms-access-
report-line-break-at-character