Saturday, August 14, 2021

Embedding an Image in an Outlook Email

 I had a project where I needed to generate some draft emails programmatically in Outlook.

Inserting the company logo and some content related images took some googling to sort through. Ideally I wanted to encode the images as Base64 strings, but Outlook does not allow this.

The code below has some html I took from an existing email. Interpolating strings into html and, worse, hand editing it, is probably not best practice, but for purposes of this demo, it works. Also, there may be more abstracted tools and libraries for working with Outlook. I'm used to using win32com, so that is my general go-to tool for Microsoft Office and other historically significant desktop Windows apps.

Screenshot of draft email that script generates:


Code:

"""

Demo of how to embed a picture in a Microsoft Outlook email.

"""


import win32com.client as win32


PR_ATTACH_CONTENT_ID = 'http://schemas.microsoft.com/mapi/proptag/0x3712001F'

PR_ATTACHMENT_HIDDEN = 'http://schemas.microsoft.com/mapi/proptag/0x7FFE000B'


PICLOC = r'C:\Users\carl.trachte\Documents\paintbrush.png'


BODYFORMAT = """

<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">

   <head>

      <meta http-equiv=Content-Type content="text/html; charset=us-ascii">

      <meta name=Generator content="Microsoft Word 15 (filtered medium)">

      <!--[if !mso]>

      <style>v\:* {{behavior:url(#default#VML);}}

         o\:* {{behavior:url(#default#VML);}}

         w\:* {{behavior:url(#default#VML);}}

         .shape {{behavior:url(#default#VML);}}

      </style>

      <![endif]-->

      <style>

         <!--

            /* Font Definitions */

            @font-face

            {{font-family:"Cambria Math";

            panose-1:2 4 5 3 5 4 6 3 2 4;}}

            @font-face

            {{font-family:Calibri;

            panose-1:2 15 5 2 2 2 4 3 2 4;}}

            /* Style Definitions */

            p.MsoNormal, li.MsoNormal, div.MsoNormal

            {{margin:0in;

            font-size:11.0pt;

            font-family:"Calibri",sans-serif;}}

            span.EmailStyle17

            {{mso-style-type:personal-compose;

            font-family:"Calibri",sans-serif;

            color:windowtext;}}

            .MsoChpDefault

            {{mso-style-type:export-only;

            font-family:"Calibri",sans-serif;}}

            @page WordSection1

            {{size:8.5in 11.0in;

            margin:1.0in 1.0in 1.0in 1.0in;}}

            div.WordSection1

            {{page:WordSection1;}}

            -->

      </style>

      <!--[if gte mso 9]>

      <xml>

         <o:shapedefaults v:ext="edit" spidmax="1026" />

      </xml>

      <![endif]--><!--[if gte mso 9]>

      <xml>

         <o:shapelayout v:ext="edit">

            <o:idmap v:ext="edit" data="1" />

         </o:shapelayout>

      </xml>

      <![endif]-->

   </head>

   <body lang=EN-US link="#0563C1" vlink="#954F72" style='word-wrap:break-word'>

      <div class=WordSection1>

         <table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 style='margin-left:-1.5pt;border-collapse:collapse'>

            <tr style='height:14.5pt'>

            </tr>

         </table>

         {0:s}

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

      </div>

      </div>

   </body>

</html>"""


GRAPHICFRAME = """

      <div class=WordSection1>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

         <p class=MsoNormal>

            <b>

               <o:p>&nbsp;</o:p>

            </b>

         </p>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

         <p class=MsoNormal>

            <img width=410 height=410 style='width:4.2666in;height:4.2666in' id="Picture_x0020_2" src="cid:{0:s}" alt="Chart&#10;&#10;Description automatically generated">

            <o:p></o:p>

         </p>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

         <p class=MsoNormal>

            <o:p>&nbsp;</o:p>

         </p>

"""



def getoutlook():

    """

    Return Outlook object.

    """

    return win32.gencache.EnsureDispatch('outlook.application')


def makeemail(outlookobject, text, subject, recipient):

    """

    Return e-mail object

    """

    mail = outlookobject.CreateItem(0)

    mail.To = recipient

    mail.Subject = subject

    mail.HTMLBody = text

    return mail


def addlogoshow(mailobject):

    """

    Embed cid image in e-mail.


    Save e-mail and bring up in window.

    """

    attachmnt = mailobject.Attachments.Add(PICLOC, win32.constants.olByValue, 0, 'paintbrush.png')

    attachmnt.PropertyAccessor.SetProperty(PR_ATTACH_CONTENT_ID, 'paintbrush.png')

    attachmnt.PropertyAccessor.SetProperty(PR_ATTACHMENT_HIDDEN, False)

    mailobject.Save()

    mailobject.Display()

    mailobject.Save()

outlook = getoutlook()

htmlbody = BODYFORMAT.format(GRAPHICFRAME.format('paintbrush.png'))

mail = makeemail(outlook, htmlbody, 'blah', 'XXXXXXXX@gmail.com')

addlogoshow(mail)