How to set Journal Body image and save it to pst

how to set Journal Body image and save it to pst in Aspose.email for .NET. Also give all property to set body image.

I’m using :
MapiJournal journal = new MapiJournal();
if (journal_fields.BodyType == “HTML”)
{
foreach (var item in journal_fields.BodyRefDict)
{
string imgPath = item.Key;
string contentId = item.Value;
if (!File.Exists(imgPath))
continue;
byte[] fileBytes = File.ReadAllBytes(imgPath);
journal.Attachments.Add(Path.GetFileName(imgPath), fileBytes);
var attach = journal.Attachments[journal.Attachments.Count - 1];
attach.SetProperty(new MapiProperty(MapiPropertyTag.PR_ATTACH_CONTENT_ID_W, Encoding.Unicode.GetBytes(contentId)));
attach.SetProperty(new MapiProperty(MapiPropertyTag.PR_ATTACH_FLAGS, BitConverter.GetBytes(4)));
attach.SetProperty(new MapiProperty(MapiPropertyTag.PR_ATTACHMENT_HIDDEN, BitConverter.GetBytes(true)));
attach.SetProperty(new MapiProperty(MapiPropertyTag.PR_RENDERING_POSITION, BitConverter.GetBytes(-1)));

}
journal.SetBodyContent(htmlBody, BodyContentType.Html);

}
using (MemoryStream ms = new MemoryStream())
{
journal.Save(ms);
ms.Position = 0;

MapiMessage finalMsg = MapiMessage.Load(ms);
folder.AddMessage(finalMsg);

}

Hello @kumarpiyush01,

Could you describe the exact symptom you are seeing? Is the image not displayed at all, shown as a separate attachment, or does it fail only in Outlook while reading back through Aspose works? Since the code path itself is sound, the answer depends on where it breaks.

Two improvements over your version — no MemoryStream round-trip, and typed property access:

using Aspose.Email;
using Aspose.Email.Mapi;
using Aspose.Email.Storage.Pst;

string imgPath   = @"C:\data\logo.png";
string contentId = "logo001@aspose";          // must match the cid: in the HTML

string htmlBody =
    "<html><body>" +
    "<p>Phone call with the customer.</p>" +
    "<p><img src=\"cid:" + contentId + "\" width=\"64\" height=\"64\"></p>" +
    "</body></html>";

var journal = new MapiJournal
{
    Subject          = "Call to John",
    StartTime        = new DateTime(2026, 7, 21, 10, 0, 0, DateTimeKind.Utc),
    EndTime          = new DateTime(2026, 7, 21, 10, 30, 0, DateTimeKind.Utc),
    BriefDescription = "Phone call",          // the "Entry type" field in Outlook
    Description      = "Discussed the renewal.",
    DocumentStatus   = MapiJournalDocumentStatus.Saved,
    Categories       = new[] { "Business" }
};

journal.SetBodyContent(htmlBody, BodyContentType.Html);

string fileName = Path.GetFileName(imgPath);
journal.Attachments.Add(fileName, File.ReadAllBytes(imgPath));
MapiAttachment attach = journal.Attachments[journal.Attachments.Count - 1];

attach.SetProperty(KnownPropertyList.AttachContentId,  contentId);   // <- the essential one
attach.SetProperty(KnownPropertyList.AttachMethod,     1);           // ATTACH_BY_VALUE
attach.SetProperty(KnownPropertyList.AttachFlags,      4);           // ATT_MHTML_REF
attach.SetProperty(KnownPropertyList.AttachMimeTag,    "image/png");
attach.SetProperty(KnownPropertyList.AttachmentHidden, true);
attach.SetProperty(KnownPropertyList.RenderingPosition, -1);
attach.SetProperty(KnownPropertyList.AttachLongFilename, fileName);
attach.SetProperty(KnownPropertyList.AttachExtension,  Path.GetExtension(fileName));
attach.SetProperty(KnownPropertyList.DisplayName,      fileName);

using (PersonalStorage pst = PersonalStorage.Create(@"C:\data\journal.pst", FileFormatVersion.Unicode))
{
    FolderInfo folder = pst.CreatePredefinedFolder("Journal", StandardIpmFolder.Journal);
    folder.AddMapiMessageItem(journal);   // takes MapiJournal directly, keeps journal-specific props
}

Why the two changes:

  • AddMapiMessageItem(IMapiMessageItem) accepts the MapiJournal directly, so you can drop the Save(ms)MapiMessage.Load(ms) round-trip entirely.
  • SetProperty(PropertyDescriptor, object) writes the correct MAPI type for you. In your version, BitConverter.GetBytes(true) produces 1 byte while PT_BOOLEAN is a 2-byte value, and Encoding.Unicode.GetBytes(cid) has no terminating null. Both happen to survive today, but the typed overload removes the risk.

One last thing worth flagging if this is going into production: Journal has been deprecated since Outlook 2013, and it is absent from new Outlook and OWA entirely.

In Normal folder (folder which is not a type of journal ) saving through your code : using (PersonalStorage pst = PersonalStorage.Create(@“C:\data\journal.pst”, FileFormatVersion.Unicode))
{
FolderInfo folder = pst.CreatePredefinedFolder(“Journal”, StandardIpmFolder.Journal);
folder.AddMapiMessageItem(journal); // takes MapiJournal directly, keeps journal-specific props
} throws an Exception : MessageClass of the item to be added (IPM.Activity) doesn’t correspond to the folder’s ContainerClass (IPF.Note).

attach.SetProperty(KnownPropertyList.AttachContentId, contentId); // ← the essential one
attach.SetProperty(KnownPropertyList.AttachMethod, 1); // ATTACH_BY_VALUE
attach.SetProperty(KnownPropertyList.AttachFlags, 4); // ATT_MHTML_REF
attach.SetProperty(KnownPropertyList.AttachMimeTag, “image/png”);
attach.SetProperty(KnownPropertyList.AttachmentHidden, true);
attach.SetProperty(KnownPropertyList.RenderingPosition, -1);
attach.SetProperty(KnownPropertyList.AttachLongFilename, fileName);
attach.SetProperty(KnownPropertyList.AttachExtension, Path.GetExtension(fileName));
attach.SetProperty(KnownPropertyList.DisplayName, fileName); using the above the body image is still lost(not present after saving).

@kumarpiyush01,

Please allow us some time to investigate this issue. We will get back to you with an update as soon as we have more information.

Thank you.

@kumarpiyush01,

Thanks for the follow-up, we reproduced this locally. The image is not being lost on save; it is never rendered, because a journal item cannot display a cid: image at all.

IPM.Activity is an RTF-body item type. Outlook renders its body from PR_RTF_COMPRESSED and does not resolve cid: references against the attachment table, that resolution happens only for HTML-body messages.

Put the image bytes straight into the RTF as a \pict group. No attachment, no content ID, no AttachFlags.

using System;
using System.IO;
using System.Text;
using Aspose.Email;
using Aspose.Email.Mapi;
using Aspose.Email.Storage.Pst;

string imgPath = @"C:\data\logo.png";

var journal = new MapiJournal
{
    Subject          = "Call to John",
    StartTime        = new DateTime(2026, 7, 21, 10, 0, 0, DateTimeKind.Utc),
    EndTime          = new DateTime(2026, 7, 21, 10, 30, 0, DateTimeKind.Utc),
    BriefDescription = "Phone call",                       // "Entry type" in Outlook
    DocumentStatus   = MapiJournalDocumentStatus.Saved,
    Categories       = new[] { "Business" },
};

journal.SetBodyContent(
    BuildRtfWithPicture(File.ReadAllBytes(imgPath), 64, 64),
    BodyContentType.Rtf);

using (var pst = PersonalStorage.Create(@"C:\data\journal.pst", FileFormatVersion.Unicode))
{
    var folder = pst.CreatePredefinedFolder("Journal", StandardIpmFolder.Journal);
    folder.AddMapiMessageItem(journal);
}

static string BuildRtfWithPicture(byte[] png, int pxWidth, int pxHeight)
{
    var sb = new StringBuilder();
    sb.Append(@"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Calibri;}}\viewkind4\uc1");
    sb.Append(@"\pard\f0\fs22 Phone call with the customer.\par");

    // \pngblip for PNG, \jpegblip for JPEG; goal sizes are in twips (1 px = 15 twips at 96 dpi)
    sb.Append(@"{\pict\pngblip")
      .Append(@"\picw").Append(pxWidth).Append(@"\pich").Append(pxHeight)
      .Append(@"\picwgoal").Append(pxWidth * 15).Append(@"\pichgoal").Append(pxHeight * 15)
      .Append(' ');
    foreach (byte b in png) sb.Append(b.ToString("x2"));   // hex, lowercase
    sb.Append('}');

    sb.Append(@"\par -- end --\par}");
    return sb.ToString();
}

Notes on the RTF: the picture data must be plain lowercase hex, \pngblip and \jpegblip are the two formats Outlook accepts directly, and \picwgoal/\pichgoal (twips) control the displayed size while \picw/\pich state the native pixel size. Repeat the {\pict ...} group for each image, anywhere in the text flow.

Adding to a non-journal folder

AddMapiMessageItem enforces that the item’s MessageClass matches the folder’s ContainerClass, which is why you get:

MessageClass of the item to be added (IPM.Activity) doesn’t correspond to the folder’s ContainerClass (IPF.Note).

To place a journal item into an ordinary IPF.Note folder, bypass that check by adding the underlying message:

folder.AddMessage(journal.GetUnderlyingMessage());