Embedding images using CID refs with Outlook MailItem object not displaying in email

Question:

Trying to use an Outlook signature through Python (using 3.10.6 for this example), however the images within the signature are not being displayed. Just shows the typical broken image with "The picture can’t be displayed."

Here is what I am using to create the draft email, where full_path_body_content is the html signature text that’s been previously modified to use absolute paths for all its image refs.

from win32com.client import Dispatch
from lxml import html

outlook = Dispatch("outlook.application")
mail = outlook.CreateItem(0)
mail.To = recipients
mail.CC = cc_recipients
mail.BCC = bcc_recipients
mail.Subject = subject

root = html.fromstring(full_path_body_content)
for img_tag in root.xpath("//img"):
    src = os.path.abspath(img_tag.get("src"))
    if not src.startswith("http"):
        attachment = mail.Attachments.Add(src)

        cid = str(os.path.basename(src)).split(".")[0]              
        attachment.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F", cid)

        img_tag.set("src", f"cid:{cid}")

modified_body_content = html.tostring(root, method="html", encoding="unicode")
mail.HTMLBody = modified_body_content

for attachment_path in attachments:
    mail.Attachments.Add(attachment_path)

mail.Save()

So this works fine with creating the draft, adding my attachments, and displaying the text, but the images are all broken. If I simply remove the line attachment.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F", cid) then I do see the images actually attaching to my email draft in Outlook, and if I open the attachment I see the expected image just fine. So this means the part of adding the files from a path is correct, its just assigning the files CID to my body doesn’t seem to be working.

I can confirm from debugging though that the modified_body_content does reference the CID images in the body. i.e. Within modified_body_content I can see that an img will have src="cid:image002" which should be correct.

During debugging I have also confirmed that the attachments do have the property properly assigned by checking [attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F") for attachment in mail.Attachments] which gives the following output for my images: ['image002', 'image004', 'image006', 'image008', 'image010'].

So given that the images should attach fine, the CID is assigned properly for each image, and the body content properly references the CID in the src, I am at a bit of a loss.

Only other thing I can mention is that when I remove the .SetProperty line the images are attached as actual attachments I can see, and if I save the draft as HTML I can see the images in the files folder it creates. However when I do set the CID property, the images are not shown (as expected) but when I save the draft as HTML I do see it creates the files with appropriate width and height for the images I expect to see, but they’re all black and 0 bytes in size. So maybe it isn’t attaching them correctly, or maybe that’s just expected output from embedded images.

Edit: Using OutSpy I was able to confirm the CID and attachments were properly linked.

Asked By: rexcision

||

Answers:

Email signature I was using had VML formatting that my desktop application was trying to use over the img tags, whereas the web version must prefer img over VML. Never heard of VML before so didn’t think much of it as I was trying to debug this. Ended up using regex:

re.sub(r'<!--[if gte vml.*?-->', "", html_body_content, flags=re.DOTALL)

along with lxml


for img_tag in root.xpath("//img"):
    if 'v:shapes' in img_tag.attrib:
        del img_tag.attrib['v:shapes']

Which removes all the v:shapes attributes in my images and all of the if statements that try to display the VML. Embedded images now displaying perfectly.

Takeaway for anyone in similar position: Keep it simple. Test out a simple e-mail before complaining that a complex one doesn’t work.

Answered By: rexcision
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.