Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Trouble with TIdMessageBuilderHtml and inline images


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 8 - Last Post: Dec 6, 2016 10:12 AM Last Post By: pss edn
pss edn

Posts: 45
Registered: 1/13/04
Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2016 7:42 AM
Using XE7 and Indy that comes with it.
I am using the code below (the e-mail creation part) to send html messages that contain images.
Although the code is working correctly with some e-mail clients, like Office Outlook, other e-mail clients like in mail.google.com or outlook.live.com does not.
In those cases, the image either apears as a line with the "X" symbol, rectangle with the full dimension of the image and the "X" symbol, or a rectangle with the full dimension of the image, no "X" symbol, and the "alt" text of the image inside the rectangle.
The google and outlook web e-mail clients have indeed other e-mails that contain images and render correctly, but not mine.
Googling around for information about or sample code on how to use TIdMessageBuilderHtml, all that I can find is a basic skeleton on how to do things.
So, how can I make TIdMessageBuilderHtml be outlook and gmail compatible.

var
iIdx: SmallInt;
iK: SmallInt;
sTx: String;
IdEml: TIdMessage;
MsgBuilder: TIdCustomMessageBuilder;

begin
IdEml := TIdMessage.Create(nil);
IdEml.Clear;
IdEml.From.Name := sFromDisplayName;
IdEml.From.Address := sFrom;
IDEml.ReplyTo.EMailAddresses := sFrom;
IdEml.Subject := sSubject;
IdEml.Recipients.Add;
IdEml.Recipients.Items[0].Address := sTo;
IDEml.Charset := 'utf-8';
IdEml.Body.Clear;
IdEml.ContentType := 'multipart/relative'; // Composite HTML with objects - ContentType set to 'multipart/relative'

MsgBuilder := TIdMessageBuilderHtml.Create;
TIdMessageBuilderHtml(MsgBuilder).Html.Text := sMessage;
TIdMessageBuilderHtml(MsgBuilder).HtmlCharSet := 'utf-8';

// Inline images
iIdx := 0;
While Length(sImages) > 0 do
begin
iK := Pos(';', sImages);
If iK > 0 Then
begin
sTx := LeftStr(sImages, iK - 1);
TIdMessageBuilderHtml(MsgBuilder).HtmlFiles.Add(sTx);
TIdMessageBuilderHtml(MsgBuilder).HtmlFiles[iIdx].ContentType := 'image/' + LowerCase(MidStr(ExtractFileExt(sTx), 2, 99));
TIdMessageBuilderHtml(MsgBuilder).HtmlFiles[iIdx].ContentID := '<cid:' + ExtractFileName(sTx) + '>';
Inc(iIdx);
// Next image
sImages := MidStr(sImages, iK + 1, 9999);
end;
sImages := '';
end;
MsgBuilder.FillMessage(IdEml);
end;
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2016 10:58 AM   in response to: pss edn in response to: pss edn
pss wrote:

I am using the code below (the e-mail creation part) to send html
messages that contain images.

Why are you doing so much type-casting? Your MsgBuilder variable should
be declared as TIdMessageBuilderHtml instead of TIdCustomMessageBuilder.

You are also leaking your TIdMessage and TIdMessageBuilderHtml objects, since
you are not Free()'ing them.

Although the code is working correctly with some e-mail clients,
like Office Outlook, other e-mail clients like in mail.google.com or
outlook.live.com does not.

You are not setting the TIdMessageBuilderAttachment.ContentID property correctly.
Don't set the value to '<cid:...>'. The 'cid:' prefix should only be used
in the "href" attribute of an HTML "<img>" element, eg:

<img href="cid:image1.jpg" ... />


The ContentID property value should be set to just the actual ID by itself:

...ContentID := 'image1.jpg';


In those cases, the image either apears as a line with the "X" symbol,
rectangle with the full dimension of the image and the "X" symbol, or
a rectangle with the full dimension of the image, no "X" symbol, and
the "alt" text of the image inside the rectangle.

That means the email reader can't find the attachment, which makes sence
if you use malformed ContentID property values.

The google and outlook web e-mail clients have indeed other e-mails
that contain images and render correctly, but not mine.

Have you looked at the raw content of the working emails to see what is different
between them and the non-working emails?

So, how can I make TIdMessageBuilderHtml be outlook and gmail
compatible.

Try this instead:

var
  sTx: String;
  IdEml: TIdMessage;
  MsgBuilder: TIdMessageBuilderHtml;
begin
  IdEml := TIdMessage.Create(nil);
  try
    IdEml.From.Name := sFromDisplayName;
    IdEml.From.Address := sFrom;
    IdEml.ReplyTo.EMailAddresses := sFrom;
    IdEml.Subject := sSubject;
    IdEml.Recipients.Add.Address := sTo;
    MsgBuilder := TIdMessageBuilderHtml.Create;
    try
      MsgBuilder.Html.Text := sMessage;
      MsgBuilder.HtmlCharSet := 'utf-8';
 
      // Inline images
      while sImages <> '' do
      begin
        sTx := Fetch(sImages, ';');
        if sTx <> '' then
        begin
          with MsgBuilder.HtmlFiles.Add(sTx) do
          begin
            ContentType := GetMIMETypeFromFile(sTx);
            ContentID := ExtractFileName(sTx);
          end;
        end;
      end;
 
      { alternatively:
 
      slImages := TStringList.Create;
      try
        SplitColumns(sImages, slImages, ';');
        or
        SplitDelimitedString(sImages, slImages, True, ';');
 
        for idx := 0 to slImages.Count-1 do
        begin
          sTx := slImages[idx];
          ...
        end;
      finally
        slImages.Free;
      end;
      }
 
      MsgBuilder.FillMessage(IdEml);
    finally
      MsgBuilder.Free;
    end;
    // use IdEml as needed...
  finally
    IdEml.Free;
  end;
end;


Alternatively:

var
  sTx: String;
  IdEml: TIdMessage;
  MsgBuilder: TIdMessageBuilderHtml;
begin
  IdEml := nil;
  try
    MsgBuilder := TIdMessageBuilderHtml.Create;
    try
      MsgBuilder.Html.Text := sMessage;
      MsgBuilder.HtmlCharSet := 'utf-8';
 
      // Inline images
      while sImages <> '' do
      begin
        sTx := Fetch(sImages, ';');
        if sTx <> '' then
        begin
          with MsgBuilder.HtmlFiles.Add(sTx) do
          begin
            ContentType := GetMIMETypeFromFile(sTx);
            ContentID := ExtractFileName(sTx);
          end;
        end;
      end;
 
      { alternatively:
 
      slImages := TStringList.Create;
      try
        SplitColumns(sImages, slImages, ';');
        or
        SplitDelimitedString(sImages, slImages, True, ';');
 
        for idx := 0 to slImages.Count-1 do
        begin
          sTx := slImages[idx];
          ...
        end;
      finally
        slImages.Free;
      end;
      }
 
      IdEml := MsgBuilder.NewMessage(nil);
    finally
      MsgBuilder.Free;
    end;
 
    IdEml.From.Name := sFromDisplayName;
    IdEml.From.Address := sFrom;
    IdEml.ReplyTo.EMailAddresses := sFrom;
    IdEml.Subject := sSubject;
    IdEml.Recipients.Add.Address := sTo;
 
    // use IdEml as needed...
  finally
    IdEml.Free;
  end;
end;


--
Remy Lebeau (TeamB)
pss edn

Posts: 45
Registered: 1/13/04
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2016 12:11 PM   in response to: pss edn in response to: pss edn
Thanks for your allways instructive and valuable responses.

1.) The typecasting is done because in the same routine I detect and handle text only messages, so with only one variable I do MsgBuilder := TIdMessageBuilderHtml.Create; and MsgBuilder := TIdMessageBuilderPlain.Create;

2.) The routine in the message is not complete, after sending the e-mail, there is a cleanup section, freeing al created objects.

3.) Regarding ContentID, I corrected this part following your instructions.

4.) As far as I know, I can not see any evident difference within a raw correct e-mail with images and mine. In mine, I can see things like the text "
An HTML viewer is required to see this message" that I have not inserted, and also, like in the other, all the html tags have "3D" inserted. After all html text, there are sections for the images that contains them in base64, and tags like Content-Type: image/gif; name="logo.gif" Content-Transfer-Encoding: base64, etc.

5.) As for your code, I can see a fantastic instruction like "Fetch", that I did not previously know about, and that from now on, I will use.

6.) The result after the modification of the ContentID does not solve yet the problem. When opening the e-mail in a web browser (gmail account), all html formatted text is rendered correctly. In place of the images are small rectangles with only the alt text, and the link included in the img tag. The images are shown below after all the html text in a thumbnail size, preceded with the text "2 attached files".
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 31, 2016 2:11 PM   in response to: pss edn in response to: pss edn
pss wrote:

The typecasting is done because in the same routine I detect and
handle text only messages, so with only one variable I do MsgBuilder
:= TIdMessageBuilderHtml.Create; and MsgBuilder := TIdMessageBuilderPlain.Create;

You can create a plain-text message with TIdMessageBuilderHtml by leaving
its Html and HtmlFiles properties empty and just populate its PlainText property.

4.) As far as I know, I can not see any evident difference within a
raw correct e-mail with images and mine.

Obviously, there has to be a difference, or else your emails would be displaying
images correctly.

In mine, I can see things like the text "An HTML viewer is required to
see this message" that I have not inserted

That is the default text of the HtmlViewerNeededMsg property. If you don't
want that text, you can clear the property. But you should leave it, as
the text allows non-HTML readers to display that text to the user.

and also, like in the other, all the html tags have "3D" inserted.

That is perfectly fine. The text is simply being encoded in "quoted-printable"
format.

After all html text, there are sections for the images that contains them
in base64, and tags like Content-Type: image/gif; name="logo.gif"
Content-Transfer-Encoding: base64, etc.

Also perfectly fine, provided that the HTML and image MIME parts are being
wrapped in a higher "multipart/related" MIME part (see my blog article on
"HTML Messages" at http://www.indyproject.org/sockets/blogs/RLebeau/2005_08_17_A.aspx).

5.) As for your code, I can see a fantastic instruction like "Fetch",
that I did not previously know about, and that from now on, I will
use.

Indy has many useful utility functions in its IdGlobal and IdGlobalProtocols
units. For example, you could use SplitDelimitedString() instead of a Fetch()
loop.

6.) The result after the modification of the ContentID does not solve
yet the problem. When opening the e-mail in a web browser (gmail
account), all html formatted text is rendered correctly. In place of
the images are small rectangles with only the alt text, and the link
included in the img tag. The images are shown below after all the
html text in a thumbnail size, preceded with the text "2 attached files".

Using the latest Indy snapshot, I am not able to reproduce that problem.
When I send an HTML email with embedded images to my Gmail address, it displays
the images just fine. Makes me wonder if your HTML is correct to begin with.

--
Remy Lebeau (TeamB)
pss edn

Posts: 45
Registered: 1/13/04
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 19, 2016 4:26 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Prepared a basic html e-mail with a gif image.
With this e-mail, the result is the same as explained above, in gmail, where the image sould show there is a marker, and at the bottom, after the e-mail there is a link to download the image.

Here is the content of the e-mail, ip and e-mail addresses are scrambled, and the base64 image data deleted:

Delivered-To: xxxx at gmail dot com
Received: by 10.10.10.10 with SMTP id 47csp552343qvd;
Sat, 19 Nov 2016 03:52:43 -0800 (PST)
X-Received: by 10.194.231.8 with SMTP id tc8mr2618520wjc.193.1479556362991;
Sat, 19 Nov 2016 03:52:42 -0800 (PST)
Return-Path: <zzzz at zzzz dot com>
Received: from pleskw2012-3.zzzz.es (pleskw2012-3.zzzz.es. [14.14.14.14])
by mx.google.com with ESMTP id m193si6009794wmd.4.2016.11.19.03.52.42
for <xxxx at gmail dot com>;
Sat, 19 Nov 2016 03:52:42 -0800 (PST)
Received-SPF: pass (google.com: domain of zzzz at zzzz dot com designates 19.19.19.19 as permitted sender) client-ip=11.11.11.11;
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of zzzz at zzzz dot com designates 149.62.173.9 as permitted sender) smtp.mailfrom=zzzz at zzzz dot com
Received: from Z80 ([37.223.197.132]) by pleskw2012-3.zzzz.es with MailEnable ESMTP; Sat, 19 Nov 2016 12:52:08 +0100
From: "ArchiMED -Grup de Software, s.l." <zzzz at zzzz dot com>
Subject: email html corto
To: xxxx at gmail dot com
Content-Type: multipart/alternative; boundary="GhTcZ3LgNp=_uFaMJq96x5LKyIfvYmERDn"
MIME-Version: 1.0
Reply-To: zzzz at zzzz dot com
Date: Sat, 19 Nov 2016 12:52:45 +0100
Message-ID: <65CB154AFE2A40199F46853AE840D4C3.MAI@pleskw2012-3.zzzz.es>
X-ME-Bayesian: 0.000002

This is a multi-part message in MIME format

--GhTcZ3LgNp=_uFaMJq96x5LKyIfvYmERDn
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

An HTML viewer is required to see this message

--GhTcZ3LgNp=_uFaMJq96x5LKyIfvYmERDn
Content-Type: multipart/related; type="text/html";
boundary="ErDh4m1vhlI3oX=_ktmZx6FcofyFNTYjIv"

--ErDh4m1vhlI3oX=_ktmZx6FcofyFNTYjIv
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

<html>
<head>
<title>Test email</title>
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3D=
utf-8" />
</head>
<body>
<table align=3D"center">
<tr>
<td valign=3D"top" align=3D"center">
<table width=3D"550" cellpadding=3D"0" cellspacing=3D"0">
<tr>
<td align=3D"center" style=3D"font-size:10px; color:#666;=
padding-left:10px;">
TEXT BEFORE LOGO
</td>
</tr>
<tr>
<td style=3D"background-color:#f9fafc;border-top:0px s=
olid #FFFFFF;height:65px; padding:10px;">
<img src=3D"logo.=
gif" BORDER=3D"0" title=3D"MyWeb" alt=3D"MyWeb">

</td>
</tr>
<tr>
<td valign=3D"top">

This is a sample text after the logo,<br=
/>
look if this is rendered correctly

</td>
</tr> =20
</table>
</td>
</tr>
</table>
</body>
</html>

--ErDh4m1vhlI3oX=_ktmZx6FcofyFNTYjIv
Content-Type: image/gif;
name="logo.gif"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
filename="logo.gif"
Content-ID: <logo.gif>

----->HERE GOES THE BASE64 LOGO<-----

ErDh4m1vhlI3oX=_ktmZx6FcofyFNTYjIv

GhTcZ3LgNp=_uFaMJq96x5LKyIfvYmERDn
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 21, 2016 10:23 AM   in response to: pss edn in response to: pss edn
pss wrote:

Prepared a basic html e-mail with a gif image.
With this e-mail, the result is the same as explained above,
in gmail, where the image sould show there is a marker, and
at the bottom, after the e-mail there is a link to download the
image.

As I explained to you in an earlier reply, your HTML's <img> tag needs to
use the "cid:" scheme to reference the attachment.

Change this:

<img src="logo.gif" ...>


To this instead:

<img src="cid:logo.gif" ...>


--
Remy Lebeau (TeamB)
pss edn

Posts: 45
Registered: 1/13/04
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 4, 2016 1:49 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
The result is the sane as before. Here is what I do after insertring cid:

sImg := ExtractFileName(sFullImgPath);
MsgBuilder.Html.Text := StringReplace(MsgBuilder.Html.Text, sImg, 'cid:' + sImg, [rfReplaceAll, rfIgnoreCase]);
with MsgBuilder.HtmlFiles.Add(sFullImgPath) do
begin
ContentType := GetMIMETypeFromFile(sFullImgPath);
ContentID := 'cid:' + ExtractFileName(sFullImgPath);
end;

And the e-mail comes with:
<img src=3D"cid:logo.gif" BORDER=3D"0" title=3D"MyWeb" alt=3D"MyWeb">

..../....

--hUaqzxbAnZD9vstT8RUWIVirkCsNYnN=_F
Content-Type: image/gif;
name="logo.gif"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
filename="logo.gif"
Content-ID: <cid:logo.gif>
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 5, 2016 11:59 AM   in response to: pss edn in response to: pss edn
pss wrote:

with MsgBuilder.HtmlFiles.Add(sFullImgPath) do
begin
ContentType := GetMIMETypeFromFile(sFullImgPath);
ContentID := 'cid:' + ExtractFileName(sFullImgPath);
end;

You are still making the same mistake I pointed out to you a week ago:

You are not setting the TIdMessageBuilderAttachment.ContentID property correctly.
Don't set the value to '<cid:...>'. The 'cid:' prefix should only be used
in the "href" attribute of an HTML "<img>" element, eg:

<img href="cid:image1.jpg" ... />


The ContentID property value should be set to just the actual ID by itself:

...ContentID := 'image1.jpg';


So, you need to change this line:

ContentID := 'cid:' + ExtractFileName(sFullImgPath);


To this instead:

ContentID := ExtractFileName(sFullImgPath);


Content-ID: <cid:logo.gif>

And this is where it fails, because the generated header needs to be this
instead:

Content-ID: <logo.gif>


So that "<img src="cid:logo.gif" ...> can find the attachment. "cid:" is
a protocol scheme to let the HTML reader know that the image is stored in
a related attachment, not online at some URL. It is not part of the actual
ID. "logo.gif" by itself is the actual ID. So DO NOT include 'cid:' prefix
in the value of the TIdMessageBuilderAttachment.ContentID property!

--
Remy Lebeau (TeamB)
pss edn

Posts: 45
Registered: 1/13/04
Re: Trouble with TIdMessageBuilderHtml and inline images  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 6, 2016 10:12 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Sorry, after reading your last response, this was what i wanted to do, but forgot to correct this line.
Now it is working correctly.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02