{emayili} HTML Messages with Images

No two email clients are equal. Nowhere is this more true than in the way that they treat images in HTML messages.

Some clients are fairly permissive. Thunderbird, for example, will happily display images in an HTML message if the images are included in any of the following ways:

  • a link to an URL
  • a Base64 encoded image or
  • an attached image referenced via CID.

Other clients are more restrictive. Gmail, for example, will just completely ignore Base64 encoded images.

If you are composing your HTML email in one of these clients then you really don’t need to worry about this issue because the client will normally just do the right thing and the image will appear in the message regardless of the recipient’s email client.

But if you are building and sending your message via an automated tool, say using {emayili} in R, then you need to ensure that it will render correctly in all clients. And this means that you can’t use Base64 encoded images. This is not a major restriction. However, if you are generating the contents of the HTML message using R Markdown then most likely any images are going to be Base64 encoded and embedded directly into the HTML body. And that will just not work.

The Problem

Let’s take a look at a couple of examples.

This message (where the image is referenced by URL) should work fine:

To:                          alice@gmail.com
From:                        bob@yahoo.com
Content-Type:                text/html; charset=utf-8

<html><body><img src="https://www.r-project.org/logo/Rlogo.png"></body></html>

The image referenced in the message body should appear in most email clients. The only potential risk with sending an email like this is that some time in the future the image may no longer be available at the specified URL, in which case it will no longer appear in an email client (unless, of course, there’s a cached local copy).

This message (where the image is Base64 encoded and embedded directly into the HTM) might not work on all clients:

To:                          alice@gmail.com
From:                        bob@yahoo.com
Content-Type:                text/html; charset=utf-8
Content-Transfer-Encoding:   quoted-printable

<html><body><img src=3D"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAA=
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
BJRU5ErkJggg=3D=3D"></body></html>

The principle merit to sending a message like this is that the image is immutably included in the message itself. There’s no risk of an image no longer being available at a specific URL or the content of that image being altered. However, none of this means anything if the image is not actually visible in the client!

The Solution

As of version 0.7.2 {emayili} will handle images in HTML messages in a consistent way, regardless of how those images are referenced in the original HTML. In all cases the images are added as attachments and referenced via CID. This configuration appears to work reliably across all email clients (to the best of my knowledge!).

[1] '0.7.2'

Start by creating the shell of an email message, specifying (fictional) sender and recipient.

msg <- envelope(from = "bob@yahoo.com", to = "alice@gmail.com")

Now let’s add an HTML body which references a local image.

msg %>%
  html('<img src="mini-rlogo.png">')
To:                          alice@gmail.com
From:                        bob@yahoo.com
Content-Type:                multipart/related;
                              boundary="c31cda31"

--c31cda31
Content-Type:                text/html;
                              charset=utf-8
Content-Disposition:         inline

<html><body><img src="cid:19cb1165"></body></html>

--c31cda31
Content-Type:                image/png;
                              name="file55b5a1d689102.png"
Content-Disposition:         inline; filename="file55b5a1d689102.png"
Content-Transfer-Encoding:   base64
X-Attachment-Id:             19cb1165
Content-ID:                  <19cb1165>
Content-MD5:                 07cypFEtEgmLP/5Wp+PDhw==

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
AABJRU5ErkJggg==
--c31cda31--

The resulting message is a little more complicated. Instead of simply having an HTML body, the message now has a multipart/related MIME body which includes

  1. an HTML body with an <img> tag which references an image via CID and
  2. a Base64 encoded attachment which is labelled via the Content-ID header.

That’s how it works with a local image. You can equally reference an image by URL.

msg %>%
  html('<img src="https://www.r-project.org/logo/Rlogo.png">')

In this case the image is downloaded from the URL and then attached to the message as above.

You can also specify a Base64 encoded image.

BASE64_IMG <- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAGCAMAAAA40H\
REAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3Ccu\
lE8AAAAflBMVEUAAADAwsW+wMO6vL+1t7uwsraqq7Clpqu9v8K5u7+osLxAeb45dLtliLOfoKa3uby0\
tbkla8AiaLsfZbdmg6ewsrarrbElar0fZbgdY7VsgqGqq7ClpquNl6hKdqsjZLBtgZ1LeK8eZLVVeaY\
ZX68XXawjab4hZ7sgZrj////8ro41AAAAJnRSTlMABn3Z9dl7Bp/xbnRwmob3dR+d9rrBox+364Qat9\
Da6SMv+ibTdewDpr4AAAABYktHRCnKt4UkAAAAB3RJTUUH5gENBBEUjcn84wAAADhJREFUCNdjYGRiZ\
mFlY2fg4OTi5uHlY+AXEFQTEhZhEBUTV5eQlGKQlpHVkJNXYGBgUFRSVlEFADk9AzXF1ilQAAAAJXRF\
WHRkYXRlOmNyZWF0ZQAyMDIyLTAxLTEzVDAzOjI4OjE2KzAwOjAwAkvwxAAAACV0RVh0ZGF0ZTptb2R\
pZnkAMjAxNy0xMC0xM1QxNToxMDowMiswMDowMHPFAUgAAAAASUVORK5CYII="

msg %>%
  html('<img src="{{BASE64_IMG}}">')
Date:                        Thu, 13 Jan 2022 04:07:18 GMT
X-Mailer:                    {emayili}-0.7.2
MIME-Version:                1.0
To:                          alice@gmail.com
From:                        bob@yahoo.com
Content-Type:                multipart/related;
                              boundary="3b7fb415"

--3b7fb415
Content-Type:                text/html;
                              charset=utf-8
Content-Disposition:         inline

<html><body><img src="cid:70c51a8a"></body></html>

--3b7fb415
Content-Type:                image/png;
                              name="file55b5aed91fa3.png"
Content-Disposition:         inline; filename="file55b5aed91fa3.png"
Content-Transfer-Encoding:   base64
X-Attachment-Id:             70c51a8a
Content-ID:                  <70c51a8a>
Content-MD5:                 vDyY8WtQoSM1M8sl8EBZ/g==

iVBORw0KGgoAAAANSUhEUgAAAAcAAAAGCAMAAAA40HREAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAflBMVEUAAADAwsW+wMO6vL+1
t7uwsraqq7Clpqu9v8K5u7+osLxAeb45dLtliLOfoKa3uby0tbkla8AiaLsfZbdmg6ewsrarrbEl
ar0fZbgdY7VsgqGqq7ClpquNl6hKdqsjZLBtgZ1LeK8eZLVVeaYZX68XXawjab4hZ7sgZrj////8
ro41AAAAJnRSTlMABn3Z9dl7Bp/xbnRwmob3dR+d9rrBox+364Qat9Da6SMv+ibTdewDpr4AAAAB
YktHRCnKt4UkAAAAB3RJTUUH5gENBBEUjcn84wAAADhJREFUCNdjYGRiZmFlY2fg4OTi5uHlY+AX
EFQTEhZhEBUTV5eQlGKQlpHVkJNXYGBgUFRSVlEFADk9AzXF1ilQAAAAJXRFWHRkYXRlOmNyZWF0
ZQAyMDIyLTAxLTEzVDAzOjI4OjE2KzAwOjAwAkvwxAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0x
MC0xM1QxNToxMDowMiswMDowMHPFAUgAAAAASUVORK5CYII=
--3b7fb415--

Conclusion

This new feature should make it substantially easier to formulate HTML email messages which include images. I’ve tested the new version of {emayili} fairly extensively, but I’m fully anticipating that there will still be a few minor problems. Please create an issue on the repository if you run into any trouble.

Finally, this new feature may appear a little arbitrary, but it forms the basis of the next new feature which I’ve been working on. And that I’m really excited about. Expect to hear more in the next few weeks.