VBA – Thunderbird Automation With Embedded Images

Mozilla Thunderbird Icon

Like I have done previously with Outlook Automation, I wanted to explorer the possibilities of embedding images in Thunderbird via VBA Automation


I’ve previously covered the subject of Thunderbird Automation in my article:

but this was for standard HTML content and I hadn’t, up until recently, explored what it would take to incorporate images directly in the e-mail body.

Thunderbird?

If you aren’t already aware, Thunderbird is an exceptional e-mail client brought to you by Mozilla. Yes, that’s right the same people that bring us the FireFox web browser.

Thunderbird truly is a great e-mail client easy to setup and work with and if you’re looking to get away from Outlook, this one’s worth seriously considering!

What Is Required To Embed Images Via Automation?

The short of it is that Thunderbird requires pure HTML content be passed to it.  Thus, we need to encode image in base64 and use that to build a proper HTML img tag.  So as long as we build proper HTML, it will display it properly.

Encoding an Image

Hence, we need a function, or functions, to convert an image into proper Base64 encoding. 

So we need to take an image, such as: C:\Users\DevGuy\Downloads\RedDot.png, and transform it into:

iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH5goGDg0tmf0acAAAANxJREFUGJWNjj1qw0AQhd/seKuVIGVyAxeGFLlCCBhfw/gYjo3JkUJukSKXSECdFGmZ3VlNKv/IbjLwwRTf4z0yM8M/bnbpXWeI6PS7ozCmhHR4Q5wvEOcLpNc9xpTOYVU1EbF+u7PfcDeh3+5MRCznbCQipqoYH59A3z+Tanu4B399gpnhVBUicrPvOElEkHM+i3G1vBHjagkRgapiVkqBqqLfrCGloH7/AAC0L8+ImzVCzvDeg5qmsa7r0LYthmFAjBHMDO89qqpCXdcIIcAREYgIzjkw8wnn3IQ/w+mJH97B6tIAAAAASUVORK5CYII=

which is its representation in base64 encoding.

So, I created the following 2 functions to do exactly that:

  • one to read the file and return it as a byte array
  • one to convert a byte array into a base64 encoded string
'---------------------------------------------------------------------------------------
' Procedure : ReadFileAsBinary
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Reads a file as binary content
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft ActiveX Data Objects X.X Library
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile     : Fully qualified path and filename of the file to read
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-10-05              Initial Public Release
'---------------------------------------------------------------------------------------
Public Function ReadFileAsBinary(ByVal sFile As String) As Variant
On Error GoTo Error_Handler
    '#Const EarlyBind = 1    'Use Early Binding
    #Const EarlyBind = 0    'Use Late Binding
    #If EarlyBind Then
        Dim oADOStream As ADODB.Stream
    #Else
        Dim oADOStream As Object
        Const adTypeBinary = 1
    #End If
    Dim fileBytes() As Byte

    #If EarlyBind Then
        Set oADOStream = New ADODB.Stream
    #Else
        Set oADOStream = CreateObject("ADODB.Stream")
    #End If
    With oADOStream
        .Open
        .Type = adTypeBinary
        .LoadFromFile sFile
        fileBytes() = .Read
        .Close
    End With
    ReadFileAsBinary = fileBytes()
 
Error_Handler_Exit:
    On Error Resume Next
    If Not oADOStream Is Nothing Then
        oADOStream.Close
        Set oADOStream = Nothing
    End If
    Exit Function
 
Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
           "Error Source: ReadFileAsBinary" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Function
'---------------------------------------------------------------------------------------
' Procedure : EncodeBase64
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Encodes the supplied byte array as a base64 string used for HTML img tags
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft XML, v6.0
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' fileBytes : binary byte array of the file to convert to base64
'
' Usage:
' ~~~~~~
' sB64 = EncodeBase64(ReadFileAsBinary("C:\Temp\OrgChart.jpg"))
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-10-05              Initial Public Release
'---------------------------------------------------------------------------------------
Public Function EncodeBase64(fileBytes() As Byte) As String
On Error GoTo Error_Handler
    '#Const EarlyBind = 1    'Use Early Binding
    #Const EarlyBind = 0    'Use Late Binding
    #If EarlyBind Then
        Dim oXML                      As MSXML2.DOMDocument60
        Dim oNode                     As MSXML2.IXMLDOMElement
        
        Set oXML = New MSXML2.DOMDocument60
    #Else
        Dim oXML                      As Object
        Dim oNode                     As Object
        
        Set oXML = CreateObject("MSXML2.DOMDocument.6.0")
    #End If

    Set oNode = oXML.createElement("b64")
    With oNode
        .DataType = "bin.base64"
        .nodeTypedValue = fileBytes
        EncodeBase64 = Replace(.text, vbLf, "")
    End With

Error_Handler_Exit:
    On Error Resume Next
    If Not oNode Is Nothing Then Set oNode = Nothing
    If Not oXML Is Nothing Then Set oXML = Nothing
    Exit Function
 
Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
           "Error Source: EncodeBase64" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Function

Building the Img Tag

Next, I created a simple function that took the results from the above functions and created the proper HTML img tag that we could simply call when building an e-mail’s HTML body content:

'---------------------------------------------------------------------------------------
' Procedure : TB_AddImageFile
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Convert an image file into an HTML img tag base64 entry
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile     : fully qualified path and file name of the image file to convert
'               (so far tested with jpg, gif, png and bmp)
'
' Usage:
' ~~~~~~
' sHTML = TB_AddImageFile("C:\Temp\OrgChart.jpg")
' sHTML = TB_AddImageFile("C:\Temp\OrgChart.png")
' sHTML = TB_AddImageFile("C:\Temp\OrgChart.gif")
' sHTML = TB_AddImageFile("C:\Temp\OrgChart.bmp")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2022-10-05              Initial Release
'---------------------------------------------------------------------------------------
Public Function TB_AddImageFile(sFile As String) As String
On Error GoTo Error_Handler
    Dim sOutput As String
    Dim sFileName As String
    Dim sFileExt As String
    
    sFileName = Right(sFile, Len(sFile) - InStrRev(sFile, "\"))
    sFileExt = Right(sFile, Len(sFile) - InStrRev(sFile, "."))
    sOutput = "<img src=""data:image/"
    sOutput = sOutput & sFileExt
    If sFileExt = "bmp" Then 'Special case for bmp files!
        sOutput = sOutput & ";filename=" & sFileName
    End If
    sOutput = sOutput & ";base64,"
    sOutput = sOutput & EncodeBase64(ReadFileAsBinary(sFile))
    sOutput = sOutput & """>"
    
    TB_AddImageFile = sOutput
 
Error_Handler_Exit:
    On Error Resume Next
    Exit Function
 
Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
        "Error Source: TB_AddImageFile" & vbCrLf & _
        "Error Number: " & Err.Number & vbCrLf & _
        "Error Description: " & Err.Description & _
        Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
        , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Function

So taking the above example and now encoding and building the img tag we can get

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH5goGDg0tmf0acAAAANxJREFUGJWNjj1qw0AQhd/seKuVIGVyAxeGFLlCCBhfw/gYjo3JkUJukSKXSECdFGmZ3VlNKv/IbjLwwRTf4z0yM8M/bnbpXWeI6PS7ozCmhHR4Q5wvEOcLpNc9xpTOYVU1EbF+u7PfcDeh3+5MRCznbCQipqoYH59A3z+Tanu4B399gpnhVBUicrPvOElEkHM+i3G1vBHjagkRgapiVkqBqqLfrCGloH7/AAC0L8+ImzVCzvDeg5qmsa7r0LYthmFAjBHMDO89qqpCXdcIIcAREYgIzjkw8wnn3IQ/w+mJH97B6tIAAAAASUVORK5CYII=">

which we can use in any HTML document or HTML compliant e-mail.

Putting It All Together

Now with all the pieces of the puzzles, we can easily build an e-mail with embedded images by simply doing something like:

Sub TestMe()
    On Error GoTo Error_Handler
    Dim sHTML As String

    sHTML = "<html><body>" 'Not strictly necessary, but still good practice
    sHTML = sHTML & "Hello <b>World</b>!" & vbLf
    sHTML = sHTML & "<br><br><br>" & TB_AddImageFile(Application.CurrentProject.Path & "\test.jpg") & vbLf
    sHTML = sHTML & "<br><br>" & TB_AddImageFile(Application.CurrentProject.Path & "\test.png") & vbLf
    sHTML = sHTML & "<br><br>" & TB_AddImageFile(Application.CurrentProject.Path & "\test.gif") & vbLf
    sHTML = sHTML & "<br><br>" & TB_AddImageFile(Application.CurrentProject.Path & "\test.bmp") & vbLf
    sHTML = sHTML & "</body></html>" 'Not strictly necessary, but still good practice

    Call TB_SendEmail("abc@xyz.com;def@wuv.ca;", "My Subject", sHTML)
    
    'Include in the closing of the form or database
'    If TempVars!bUseHTMLFile = True Then Kill Environ("TEMP") & "\TB_HTML.html"

Error_Handler_Exit:
    On Error Resume Next
    Exit Sub
 
Error_Handler:
    If Err.Number <> 2501 Then
        MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
            "Error Source: TestMe" & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Description: " & Err.Description & _
            Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
            , vbOKOnly + vbCritical, "An Error has Occured!"
    End If
    Resume Error_Handler_Exit
End Sub

Final Remarks

I say proper HTML in this approach because it generates valid HTML, that if you save, will render in a web browser just fine. You’ll also see other e-mail client support the same HTML format. So in fact, not only have we developed image embedding in Thunderbird, but moreover we’ve developed generating HTML pages with embedded images which can have much broader uses.

Do note however, that this approach, even when using .HTMLBody, with MS Outlook does NOT work. Outlook requires that you attach each image, set the attachment property, give them each a unique CID and reference the CID in the HTML.

Lastly, another great thing about this code is it is application independent so you can use it in Access, Excel, Outlook, PowerPoint, Word, … (any VBA environment), is bitness independent (works with both 32 and 64-bit installations) and requires no APIs. Win-win-win!

Download a Demo Database

Feel free to download a 100% unlocked copy by using the link provided below:

Disclaimer/Notes:

If you do not have Microsoft Access, simply download and install the freely available runtime version (this permits running MS Access databases, but not modifying their design):

Microsoft Access 2010 Runtime
Microsoft Access 2013 Runtime
Microsoft Access 2016 Runtime
Microsoft 365 Access Runtime

All code samples, download samples, links, ... on this site are provided 'AS IS'.

In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.

Download “VBA - Thunderbird Automation - Embedded Images - Demo.zip” Thunderbird-EmbeddedImages-Demo.zip – Downloaded 5798 times – 261.00 KB

2 responses on “VBA – Thunderbird Automation With Embedded Images

  1. Mike Wolfe

    Great article, Daniel. I was not aware of the Base64-encoding technique for embedding raw image data into HTML. I feel like I have a shiny new hammer and I need to go looking for nails now.

    1. Daniel Pineault Post author

      What’s also fun is you can do the inverse, take a web image and read the base64 and convert it back to a byte array and save it locally. I do that in my Bar Code and QR Code databases.