Editing MailItem.RTFBody through encoding - c#

I've had some success encoding the RTFbody from a MailItem using UTF8Encoding. I'm able to compose a new email, do all the new-email stuff and click send. Upon hitting send, I append the email with a tag that is also added to the categories. This all works and all through the RTFBbody.
The problem comes when I reply to RTF emails, which, for testing purposes, are just the emails I sent to my lonesome self. When I send the reply email and new tags were added, I remove the old tags first and then add the new tags. When I set the RTFBody in the reply email with the edited string that contains the new tags, I get a "not enough memory or disk space" error. This doesn't happen when I just remove the tags with the same function.
Bellow is the code I'm using:
private void ChangeRTFBody(string replaceThis, string replaceWith)
{
byte[] rtfBytes = Globals.ThisAddIn.email.RTFBody as byte[];
System.Text.Encoding encoding = new System.Text.UTF8Encoding();
string rtfString = encoding.GetString(rtfBytes);
rtfString = rtfString.Replace(replaceThis, replaceWith);
rtfBytes = encoding.GetBytes(rtfString);
Globals.ThisAddIn.email.RTFBody = rtfBytes; < // The error is here only on
// reply and only when I replace
// with new tags
}
These are the calls I make:
Delete old tag: ChangeRTFBody(lastTag, "");
Add new tag: ChangeRTFBody("}}\0", newTag + "}}\0");
Like I said, This works when I create a new email and send it, but not when I try to reply to the same email. It also seems that the size of the byte[] almost doubles after the delete. When I check it during the Delete it's at about 15k bytes and when I check during the Add it jumps to over 30k bytes. When I try to add the newly inflated byte[] to the rtfBody is when I get the error.
Thanks for any help and tips and sorry about all the reading.

I had the same problem and came across what I think is an easier way to replace text in a outlook rtf body by using the Word.Document object model. You will need to add reference of Microsoft.Office.Interop.Word to your project first.
then add using
using Word = Microsoft.Office.Interop.Word;
then your code would look like
Word.Document doc = Inspector.WordEditor as Word.Document;
//body text
string text = doc.Content.Text;
//find text location
int textLocation = text.IndexOf(replaceThis);
if(textLocation > -1){
//get range
int textLocationEnd = textLocation + replaceThis.Length;
//init range
Word.Range myRange = doc.Range(textLocation , textLocationEnd);
//replace text
myRange.Text = replaceWith
}

Related

(C#) Image in Mail not displayed in Outlook

I want to send mails with images in them. The code I've written works fine, but for some reason unknown to me it wont work with Outlook clients.
The test mail I sent was (left: Thunderbird, right: Outlook):
What my code is supposed to do is: It takes the RTF from a RichTextBox and converts it to HTML. This leaves the images embedded in the HTML as base64 strings. I extract all base64 encoded images one by one and put them into a MemoryStream which is accepted by LinkedResource. Since mail clients usually don't accept embedded images I replace the embedded image in the HTML with a content-id. Then I set some properties of LinkedResource and add it to an AlternateView. This alternate view is then added to a System.Net.Mail.MailMessage and the mail is sent.
The corresponding code:
MemoryStream mem = null;
private readonly Regex embeddedImageRegex = new Regex("src=\"data:image/.*?\"");
public MyHTMLMailMessage()
: base()
{
this.SubjectEncoding = Encoding.UTF8;
this.BodyEncoding = Encoding.UTF8;
this.IsBodyHtml = true;
}
public bool Send()
{
// create HTML View with images
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(HTML, System.Text.Encoding.UTF8, MediaTypeNames.Text.Html);
ReplaceEmbeddedImagesWithCID(htmlView);
this.AlternateViews.Add(htmlView);
this.Body = HTML;
SmtpClient client = new SmtpClient(server, port);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = String.IsNullOrEmpty(username);
try
{
client.Send(this);
return true;
}
catch (SmtpException e)
{
return false;
}
finally
{
mem?.Close();
}
}
private void ReplaceEmbeddedImagesWithCID(AlternateView altView)
{
string extension;
int imageIndex = 0;
string contentID = $"image{imageIndex}";
// go through every base64 string, create a content id and LinkedResource for it
while (embeddedImageRegex.IsMatch(HTML))
{
extension = new Regex("image/.*?;").Match(HTML).Value
.Replace("image/", "")
.Replace(";", "");
string base64img = embeddedImageRegex.Match(HTML).Value
.Replace("src=\"", "")
.Replace("\"", "")
.Split(',')[1];
HTML = embeddedImageRegex.Replace(HTML, $"src=\"cid:image{imageIndex}\"", 1);
byte[] byBitmap = Convert.FromBase64String(base64img);
mem = new MemoryStream(byBitmap);
mem.Position = 0;
LinkedResource linkedImage = new LinkedResource(mem, $"image/{extension}");
linkedImage.ContentId = contentID;
altView.LinkedResources.Add(linkedImage);
altView = AlternateView.CreateAlternateViewFromString(HTML, null, MediaTypeNames.Text.Html);
imageIndex++;
}
}
So I went through different solutions but none of them worked.
My steps so far:
I edited some registration keys in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\x.0\Outlook\Options\Mail or HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\x.0\Common
I left the image as base64 string in the HTML
There were some properties added
linkedImage.TransferEncoding = TransferEncoding.Base64;
linkedImage.ContentType.Name = contentID;
linkedImage.ContentLink = new Uri($"cid:{contentID}");
this.Headers.Add("Content-ID", $"<image{imageIndex}>");
this.Headers.Add("X-Attachment-Id", $"image{imageIndex}");
altView.TransferEncoding = TransferEncoding.QuotedPrintable;
None of this worked for me even although it seemed to help others. Did i overlook something?
Base64 images are blocked by default in Outlook.
You need to attach images to the email and set the PR_ATTACH_CONTENT_ID property on the email (the DASL name is "http://schemas.microsoft.com/mapi/proptag/0x3712001E"). See Embed Images in New Messages using a Macro for more information.
Have you confirmed it is not an image block from Trust Center Settings.
Unblock image downloads for a single message:
Click the InfoBar at the top of the message.
Click Download Pictures.
Unblock image downloads for all messages:
Outlook 2007
On the "Tools" menu, click Trust Center > Automatic Download.
Uncheck the "Don't download pictures automatically in HTML e-mail messages or RSS items" check box.
Outlook 2010 and Up:
On the "File" tab, click Options > Trust Center.
Under Microsoft Outlook Trust Center, click Trust Center Settings.
Uncheck the "Don't download pictures automatically in HTML e-mail messages or RSS items" check box.
Unblock picture downloads for all messages from a particular email address or domain:
In an open message that was sent from a particular email address or domain, right-click on a blocked item.
Do one of the following:
Click Add Sender to Safe Senders List.
Click Add the Domain [#domain] to Safe Senders List.

failed to add an attachment in MimeMessage (rotativa) for sending Mail

I want to send a (view page) pdf sending by email.but when I trying to add an attachment, I found an error below "Add". for that I can't successfully sending mail with attaching my view pdf.
Here is my code:
(Ordercontroller)
//other code
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Test Project", "pt300#gmail.com"));
message.To.Add(new MailboxAddress("psm", "p689#gmail.com"));
message.Subject = "Hi,this is demo email";
message.Body = new TextPart("plain")
{
Text = "Hello,My First Demo Mail it is.Thanks",
};
//add attach
var aa = new ViewAsPdf("Cart")
{
FileName = "Invoice.pdf", //actually, I don't know why this filename is
// "Invoice". I found this system on a website.
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait,
};
message.Attachments.Add(aa);
//end attach
using (var client = new SmtpClient())
{
client.Connect("smtp.gmail.com", 587, false);
client.Authenticate("pt300#gmail.com", "MyPassword");
client.Send(message);
client.Disconnect(true);
}
//other code
and these controller's view I trying to pdf and send by mail:
(HomeController)
public ActionResult Cart()
{
List<Product> products = HttpContext.Session.Get<List<Product>>("products");
if (products == null)
{
products = new List<Product>();
}
return View(products);
}
in OrderController I found an error.
how I will solve this problem and successfully send my view's pdf by mail.please help.
Attachments is a MimeMessage property that has no such method Add, so here comes the error. To add the attachments you should create a BodyBuilder object before, with this class you will be able to add new attachments (each one of them as byte array), usign Attachments Property and its Add method, but always related to BodyBuilder, not to MimeMessage.
Please, take a look to the answer given here, as I guess is what you are looking for:
.net Core Mailkit send attachement from array
Or check another example here:
https://www.taithienbo.com/send-email-with-attachments-using-mailkit-for-net-core/
In addition, you could get Rotativa PDF as byte array usign this code:
var rotativaAction = new ViewAsPdf("Cart")
{
FileName = "Invoice.pdf", // You can change this file name to set whatever you want
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait,
};
byte[] fileAttachmetByteArray = rotativaAction.BuildFile(ControllerContext);
Once you have generated your PDF as a byte array using Rotativa, guessing you have stored the result in a variable called fileAttachmentByteArray (as stated in the previous code sample), you must simply send this variable (the byte array) to the Add method of the BodyBuilder, as the second parameter (first is the attachement file name, which is completely free to use the name you prefer), and then set Mime Message Body, usign the BodyBuilder you have worked with.
So, to explain the process in steps:
On the first line you create a BodyBuilder variable, and initialize it, using the text you want as body for the email.
Then, you call Add method to add a new attachment, sending to it the file name you wish and your previously created bye array
Finally, you asign the value to the Body property of your Mime Message
And your code should look like this:
var builder = new BodyBuilder { HtmlBody = "yourBodyMessage" }; // Change "yourBodyMessage" to the email body you desire
builder.Attachments.Add("filename", fileAttachmentByteArray); // Once again, you can change this "filename" to set whatever you want
mimeMessage.Body = builder.ToMessageBody(); // Assuming mimeMessage is the same variable you provided in your code

Editing a pdf document and saving it using ItextSharp writes an empty pdf document [duplicate]

I can extract text from pages in a PDF in many ways:
String pageText = PdfTextExtractor.GetTextFromPage(reader, i);
This can be used to get any text on a page.
Alternatively:
byte[] contentBytes = iTextSharp.text.pdf.parser.ContentByteUtils.GetContentBytesForPage(reader, i);
Possibilities are endless.
Now I want to remove/redact a certain word, e.g. explicit words, sensitive information (putting black boxes over them obviously is a bad idea :) or whatever from the PDF (which is simple and text only). I can find that word just fine using the approach above. I can count its occurrences etc...
I do not care about layout, or the fact that PDF is not really meant to be manipulated in this way.
I just wish to know if there is a mechanism that would allow me to manipulate the raw content of my PDF in this way. You could say I'm looking for "SetContentBytesForPage()" ...
If you want to change the content of a page, it isn't sufficient to change the content stream of a page. A page may contain references to Form XObjects that contain content that you want to remove.
A secondary problem consists of images. For instance: suppose that your document consists of a scanned document that has been OCR'ed. In that case, it isn't sufficient to remove the (vector) text, you'll also need to manipulate the (pixel) text in the image.
Assuming that your secondary problem doesn't exist, you'll need a double approach:
get the content from the page as text to detect in which pages there are names or words you want to remove.
recursively loop over all the content streams to find that text and to rewrite those content streams without that text.
From your question, I assume that you have already solved problem 1. Solving problem 2 isn't that trivial. In chapter 15 of my book, I have an example where extracting text returns "Hello World", but when you look inside the content stream, you see:
BT
/F1 12 Tf
88.66 367 Td
(ld) Tj
-22 0 Td
(Wor) Tj
-15.33 0 Td
(llo) Tj
-15.33 0 Td
(He) Tj
ET
Before you can remove "Hello World" from this stream snippet, you'll need some heuristics so that your program recognizes the text in this syntax.
Once you've found the text, you need to rewrite the stream. For inspiration, you can take a look at the OCG remover functionality in the itext-xtra package.
Long story short: if your PDFs are relatively simple, that is: the text can be easily detected in the different content stream (page content and Form XObject content), then it's simply a matter of rewriting those streams after some string manipulations.
I've made you a simple example named ReplaceStream that replaces "Hello World" with "HELLO WORLD" in a PDF.
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
if (object instanceof PRStream) {
PRStream stream = (PRStream)object;
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Hello World", "HELLO WORLD").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
Some caveats:
I check if object is a stream. It could also be an array of streams. In that case, you need to loop over that array.
I don't check if there are form XObjects defined for the page.
I assume that Hello World can be easily detected in the PDF Syntax.
...
In real life, PDFs are never that simple and the complexity of your project will increase dramatically with every special feature that is used in your documents.
The C# equivalent of the code by Bruno:
static void manipulatePdf(String src, String dest)
{
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.GetPageN(1);
PdfObject pdfObject = dict.GetDirectObject(PdfName.CONTENTS);
if (pdfObject.IsStream()) {
PRStream stream = (PRStream)pdfObject;
byte[] data = PdfReader.GetStreamBytes(stream);
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace("Hello World", "HELLO WORLD")));
}
FileStream outStream = new FileStream(dest, FileMode.Create);
PdfStamper stamper = new PdfStamper(reader, outStream);
reader.Close();
}
I'll update this if it would turn out to still contain errors.
In follow-up to my previous C# code and the remark by Bruno that GetDirectObject(PdfName.CONTENTS) might as well return an array as opposed to a stream: In my particular case, this turned out to be true.
The PdfObject returned returned "true" for IsArray(). I checked and the array elements were all PdfIndirectReference.
A further look at the API yielded two useful bits of info:
PdfIndirectReference had a "Number" property, leading you to another PdfObject.
You can get to the referenced object using reader.GetPdfObject(int ref), where ref is the "Number" property of the IndirectReferenceObject
From there on out, you get a new PdfObject that you can check using IsStream() and modify as per the previously posted code.
So it works out to this (mind you, this is quick and dirty, but it works for my particular purposes...):
// Get the contents of my page...
PdfObject pdfObject = pageDict.GetDirectObject(PdfName.CONTENTS);
// Check that this is, in fact, an array or something else...
if (pdfObject.IsArray())
{
PdfArray streamArray = pageDict.GetAsArray(PdfName.CONTENTS);
for (int j = 0; j < streamArray.Size; j++)
{
PdfIndirectReference arrayEl = (PdfIndirectReference)streamArray[j];
PdfObject refdObj = reader.GetPdfObject(arrayEl.Number);
if (refdObj.IsStream())
{
PRStream stream = (PRStream)refdObj;
byte[] data = PdfReader.GetStreamBytes(stream);
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace(targetedText, newText)));
}
}
}

How to display message with special encoding

I am writing a program which I need to read text from a file and display this on the graph once mouse hover the datapoint. My problem is while I read the data from text file and show it on graph it shows some "?" instead of actual character. (cannot post images sorry)
here is my code to read from file and attempted to change encoding.(no success) :
string myString = File.ReadAllText(#"read.txt");
Encoding enc_to = Encoding.GetEncoding("iso-8859-1");
Encoding enc_from = Encoding.UTF8;
byte[] InitialBytes =enc_from.GetBytes(myString);
byte[] FinalBytes = Encoding.Convert(enc_from, enc_to, InitialBytes);
string myMessage = enc_to.GetString(FinalBytes);
Please note that I dont want to show string as MessageBox.Show rather I want to show it as tooltip.
here is the text in read.txt file :
3 stands of 5½"
here is the how it is shown :
3 stands of 5�"
Use Encoding.Default:
string myString = File.ReadAllText(#"read.txt",Encoding.Default);

Encoding - issue displaying dutch characters

I want to dynamically create an email and send it. So far so good, the problem is that it's in dutch language and it doesn't display correctly.
I am doing this:
// Body Html
if (!string.IsNullOrEmpty(emailMessage.BodyHtml))
{
var encoding = Encoding.UTF32;
byte[] byteArray = Encoding.GetEncoding("iso-8859-1").GetBytes(emailMessage.BodyHtml);
byte[] unicodeArray = Encoding.Convert(Encoding.GetEncoding("iso-8859-1"), encoding, byteArray);
emailMessage.BodyHtml = encoding.GetString(unicodeArray);
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType();
ct.MediaType = MediaTypeNames.Text.Html;
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(emailMessage.BodyHtml, ct);
mailMessage.AlternateViews.Add(htmlView);
htmlView.TransferEncoding = TransferEncoding.QuotedPrintable;
}
try
{
smtpSender.Send(mailMessage);
}
The mail should contain Financiële details but when i open the mail with outlook i see Financiele details
How to fix it?
Please have a look at the InternetCodepage property of the Outlook mailitem. I had a similar issue with a new e-Mail in which I inserted text from an existiing e-mail, in which German Umlauts didn't displaying correctly. This was solved after I set the InternetCodepage in the new e-mail to the appropriate value of the original e-mail.
See http://msdn.microsoft.com/en-us/library/office/ff860730.aspx for more information about this property and a list of possible values.

Categories