I have been spending the last few weeks dealing with Crossware, our email signature platform. It’s good (don’t worry, there’s a but coming up…) in that I am enjoying dealing with the C# code and thinking how to organize signatures. Crossware uses C# to create something called formulae (which are parts of your signtuare – First Name, Last Name, and so on – and you use C# code such as if/ else clauses, arrays, dictionaries to manage and display these). The formulae are part of blocks, and blocks are part of the eventual signature templates.
This project has been fun in that I enjoy coding and organizing and thinking about things, but the platform is very v1.0 in terms of the UI and features, and signatures are in general a pain to deal with. So the fun has been tempered with that.
Anyways, last week was spent on figuring out logos and icons in emails. Crossware lets you upload images to itself and then embed these into the formulae. I learnt that such embedded is via adding the image as Base64 to an email and then referring to it via its Content-Id. To give an example here’s how the code for an email I send looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
Content-Type: multipart/related; boundary="_004_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_"; type="multipart/alternative" MIME-Version: 1.0 --_004_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_ Content-Type: multipart/alternative; boundary="_000_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_" --_000_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_ Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable My actual email in plain text... --_000_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_ Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable <html> <head> <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Diso-8859-= 1"> <style type=3D"text/css" style=3D"display:none;"> P {margin-top:0;margin-bo= ttom:0;} </style> </head> <body dir=3D"ltr"> <div style=3D"font-family: Arial, Helvetica, sans-serif; font-size: 10pt; c= … … … <td style=3D"padding-bottom:2px; font-size:9pt; color:#545859"><span style= =3D"color:#702082"><img alt=3D"M" data-outlook-trace=3D"F:1|T:1" src=3D"cid= :44729497-5e96-4dbb-a49a-fb4540f4dba3"></span><a href=3D"tel:+xx xxxx xxxxx= 8" style=3D"text-decoration:none; color:#545859"> +xx xxxx 6= xxxxx</a> </td> </tr> </tbody> </table> <table style=3D"font-size:9pt; color:#545859; font-family:Arial,Helvetica,s= ans-serif" width=3D"100%" cellspacing=3D"0" border=3D"0"> <tbody> <tr> <td style=3D"font-size:9pt; color:#545859"> </td> </tr> </tbody> </table> </div> </div> </div> </body> </html> --_000_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_-- --_004_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_ Content-Type: image/png; name="ceFZZuO9AkuTTFevRGTg122CWImageMobile14pxpng.png" Content-Description: ceFZZuO9AkuTTFevRGTg122CWImageMobile14pxpng.png Content-Disposition: inline; filename="ceFZZuO9AkuTTFevRGTg122CWImageMobile14pxpng.png"; size=2193; creation-date="Tue, 22 Nov 2022 07:10:38 GMT"; modification-date="Tue, 22 Nov 2022 07:10:55 GMT" Content-ID: <44729497-5e96-4dbb-a49a-fb4540f4dba3> Content-Transfer-Encoding: base64 iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACx jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAUzaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8 P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pg0KPHg6 eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUg … gnrCmB8mG4PPL8vFnAAAAABJRU5ErkJggg== --_004_CWLP123MB5006D6720A0B77B3584F5CF1840D9CWLP123MB5006GBRP_-- |
There’s a lot of HTML table stuff in there you can ignore. The salient points are 1) the entire message is made up of multiple related messages; 2) the overall message has a Content-Type: multipart/related
which is what tells email clients these various parts are related; 3) and in terms of embedded images, my img
link has a source set to this weird “cid::44729497-5e96-4dbb-a49a-fb4540f4dba3
” text.
What is that? Turns out that is a Content Id URL. It refers to a Content-Id block within this email which contains the actual image. And 4) we can see that further below as Content-ID: <44729497-5e96-4dbb-a49a-fb4540f4dba3>
. The Base64 content specified by that block is the image.
Email clients thus know where to pick the embedded picture from and show the reader.
We were receiving complaints that some users are not seeing these images. While troubleshooting with my Outlook for Mac, I noticed that I too am not seeing the images when sending emails from this client (OWA and Outlook for Windows were fine). Digging into the HTML code for that I found that that’s because Outlook for Mac 1) creates a broken cid:
link, pointing not to the Content-Id but to something else (in the example above the link it created was src="cid:ceFZZuO9AkuTTFevRGTg122CWImageMobile14pxpng.png"
– you can find that text in the Content-Name or Content-Description of the image) and 2) worse, it does to add the Base64 image as a part of the email. So not only was the link wrong, had it been correct there was no image to pick up anyways! Hence the broken behaviour for some email clients.
Solution is to use hyperlinks instead for images. But I also wanted to explore embedding images manually.
Before I move on though, the above structure is why 1) some clients show an attachment icon in the email but don’t actually show an attachment (because, the image is actually present as a separate attachment to the image, just that it is classified as related to the main message) and 2) some clients may ignore the fact that the attachment is related and thus show it both in-line to the message as well as attachments to the email (this would also happen if the wrong multipart
type is used).
A disadvantage of embedding images is that since we are converting the image to Base64 that increases the size of the image by about 33%. So it is only recommended for smaller images. Plus older browsers like IE8 have limitations with it. In fact, other versions of IE and also older versions of Edge have size limitations with it.
Apart from the Content-Id way of embedding, one can also convert the image to Base64 oneself and insert it as the source of an img
link as a data URI. Like thus:
1 |
<img src=\"data:image/png;base64,<insert base64>\" alt=\"M\" > |
Converting an image to Base64 is easy. Here’s some PowerShell to do it:
1 2 3 4 5 6 7 |
param([Parameter(Mandatory=$true)][String]$Path) if ($PSVersionTable.PSVersion.Major -gt 5) { [convert]::ToBase64String([System.IO.File]::ReadAllBytes($Path)) } else { [convert]::ToBase64String((Get-Content $Path -Encoding Byte)) } |
For completeness, converting Base64 to an image (or any file) is easy too:
1 2 3 4 5 6 7 |
param([Parameter(Mandatory=$true)][String]$Path, [Parameter(Mandatory=$true)][String]$OutputFile) $base64 = Get-Content $Path $bytes = [Convert]::FromBase64String($base64) [System.IO.File]::WriteAllBytes($OutputFile,$bytes) # $OutputFile must be a full path |
Again, I ran into an edge case with Outlook for Mac. If I don’t specify a size in the img
tag it puts one of its own. So I should ideally do the following:
1 |
<img src=\"data:image/png;base64,<insert base64>\" width=\"xx\" height=\"yy\" alt=\"M\" > |
Not the end of the world, but this means for each picture I should also note the size and add it.
Surprisingly, this actually ended up working! Outlook for Mac still had some gotchas in that when I reply to an email with embedded images it wouldn’t treat the image background as transparent and instead show it as black. We finally went with linked images, but I spent all this time learning embedded images so I figured I must blog it here for posterity. :)