Editing Outlook.MailItem's RTFBody with C# - c#

I'm trying to append a string to an outgoing Outlook.MailItem. In the send event handler I have:
switch (mailItem.BodyFormat) {
case Outlook.OlBodyFormat.olFormatRichText:
byte[] mailItemBytes = mailItem.RTFBody as byte[];
System.Text.Encoding encoding = new System.Text.ASCIIEncoding();
string RTF = encoding.GetString(mailItemBytes);
RTF += "my string";
byte[] moreMailItemBytes = encoding.GetBytes(RTF);
mailItem.RTFBody = moreMailItemBytes;
break;
// ...
}
but the received email does not contain my string.

I know this is old and has green check already but after searching for similar issues I found a page that gives a good answer for how to modify the RTF body in an outlook project using the Word.Document object model. https://www.add-in-express.com/forum/read.php?FID=5&TID=12738
Basically you treat the text a word doc and forget about working with RTF all together. You will need to add reference of Microsoft.Office.Interop.Word to your project first.
then add using to your project
using Word = Microsoft.Office.Interop.Word;
then add your code
Word.Document doc = Inspector.WordEditor as Word.Document;
//text body
string text = doc.Content.Text;
//end of file
int endOfFile = (text.Length) > 0 ? text.Length - 1 : 0;
//Select the point to add or modify text
Word.Range myRange = doc.Range(endOfFile, endOfFile);
//add your text to end of file
myRange.InsertAfter("my string");

RTF is a pretty elaborate format for files and isn't going to be as easy as concatenating a string. You may try using the RichTextBox control and importing the data there first, adding text to it, then re-grabbing the formatted value back. A bit clunky, but a lot easier than parsing an RTF file.
As an alternative, you may be able to find a library that parses and works with RTF, but that means a dependency for your application and most likely another DLL to include in the release.

Related

How can I edit the string contents of a rich text item in the clipboard in C#?

I am working on automating a process within my business, part of which is sending an email through SalesForce. We don't have access to the SF API and the email has to be sent through salesforce in order to keep the communication searchable for the coworkers.
I need to use a template which can be selected in SalesForce, however this function does not work in IE (which our RPA solution uses) so I need to build this email from scratch.
I see two options for this:
Use the HTML to recreate the format with the right variables. This entails inserting/injecting/manipulating HTML.
Copy the format into memory/the clipboard, edit it programatically and paste it into the SF interface
This question will be about option 2. I have posted an additional question with regards to the first option separately. It can be found here.
Now on to the question: I found that I can copy the text in the template from the SalesForce webpage to the clipboard (using CTRL+C) and then paste it into word, doing so will preserve the formatting and layout of the text as well as included images. I can then copy and paste this back from word to the SF webpage as well preserving the images, format and layout. This means that the formatting and image data is retained in the clipboard.
Now I need to edit the template text before I paste it into the webpage in order to use this as a solution for automation. I have been experimenting with this for the past week but I can't find a way to edit the text while preserving the formatting and layout.
I use C# and know how to GetText and SetText as wel as images (separately) from the clipboard. However the text is then a plain string without any layout and formatting. I want to replace certain keywords in the template with the required variables.
At this point I have the following code to investigate the contents of the clipboard:
// Initialise a DataObject
DataObject t = null;
try
{
// Get Data from clipboard
t = (DataObject)Clipboard.GetDataObject(); // GetData(DataFormats.Html);
// Get formats in the DataObject
string[] tFormats = t.GetFormats();
// For each format that was found create a separate object (I did this manually, I
// inspected the formats earlier by eye.
object objectDescriptor = t.GetData("Object Descriptor", true);
object rtf = t.GetData("Rich Text Format", true);
object HTMLFormat = t.GetData("HTML Format", true);
object sysString = t.GetData("System.String", true);
object unicodeText = t.GetData("UnicodeText", true);
object text = t.GetData("Text", true);
object enhancedMetafile = t.GetData("EnhancedMetafile", true);
object metaFilePict = t.GetData("MetaFilePict", true);
object embedSource = t.GetData("Embed Source", true);
object linkSource = t.GetData("Link Source", true);
object linkSourceDescriptor = t.GetData("Link Source Descriptor", true);
object objectLink = t.GetData("ObjectLink", true);
// Try replacing the text
HTMLFormat = (object)HTMLFormat.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("HTML Format", HTMLFormat);
sysString = (object)sysString.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("System.String", sysString);
unicodeText = (object)unicodeText.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("UnicodeText", unicodeText);
text = (object)text.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("Text", text);
Clipboard.SetDataObject(t);
}
catch (exception ex)
{
// Do exception handling
}
Inspecting all these objects I see that, even though they are apparently included in the DataObject, they are all null, except for HTML Format, System.String, UnicodeText and Text. Within these options the HTML Format data is the only one that appears to have some formatting data in there (I don't know where the included image is stored though). I am able to replace the keyword in that HTML Format succesfully, however if I set it back into the DataObject and then extract the HTML format again nothing has changed. The same goes for the other text items.
I also tried changing the text in the same way for all the text items. This however has the same results. If I then try to paste the clipboard contents into Word it freezes for a little while and then nothing happens.
How can I edit this DataObject? And will that actually translate into a properly formatted and layout-ed text including the image and hyperlinks, or is this approach a dead end?
Cheers!

Add text and picture in .docx file

I use a Office Word file (template) and in this file there is repetitive default text and photo that I have to replace it by another photo and text
How can I define specific zone in the template and then find those zones in C# to replace them ?
I think the best way is to find out how to manipulate the word xml structure to include the data you want.
For template filling and altering you can use the XML SDK from Microsoft
You can also follow this manual approach here without using the SDK.
Manual approach. You will add a custom XML Ressource that includes your changes/ressources for the template.
If you don`t need to be that flexible you can use the standard content control / picture content control in Word and replace them afterwards in C# - it depends how flexible you want to be in replacing elements..
You can find a good and complete example of using picture content control here: Picture content control handling
Ok, finally I try this approch ; use a Word file with Content Control and use a XML file to bind data to them
For that I use the following code :
string outFile = #"D:\template_created.docx";
string docPath = #"D:\template.docx";
string xmlPath = #"D:\template.xml";
File.Copy(docPath, outFile);
using (WordprocessingDocument doc = WordprocessingDocument.Open(outFile, true))
{
MainDocumentPart mdp = doc.MainDocumentPart;
if (mdp.CustomXmlParts != null)
{
mdp.DeleteParts<CustomXmlPart>(mdp.CustomXmlParts);
}
CustomXmlPart cxp = mdp.AddCustomXmlPart(CustomXmlPartType.CustomXml);
FileStream fs = null;
try
{
fs = new FileStream(xmlPath, FileMode.Open);
cxp.FeedData(fs);
mdp.Document.Save();
}
finally
{
if (fs != null)
{
fs.Dispose();
}
}
}
When I run the app, it created the custom XML file and append it to my Word file. When I open the Word file, there is no error, but all the Content Control are not filled
My final approach was to use Content Control in my Word document with a unique id. Then I can find those id's with C# and replace the content.

Converting html strings in Excel file to formatted word file with .NET

Input are Excel files - the cells may contain some basic HTML formatting like <b>, <br>, <h2>.
I want to read the strings and insert the text as formatted text into word documents, i.e. <b>Foo</b> would be shown as a bold string in Word.
I don't know which tags are used so I need a "generic solution", a find/replace approach does not work for me.
I found a solution from January 2011 using the WebBrowser component. So the HTML is converted to RTF and the RTF is inserted into Word. I was wondering if there is a better solution today.
Using a commercial component is fine for me.
Update
I came across Matthew Manela's MarkupConverter class. It converts HTML to RTF. Then I use the clipboard to insert the snippet into the word file
// rtf contains the converted html string using MarkupConverter
Clipboard.SetText(rtf, TextDataFormat.Rtf);
// objTable is a table in my word file
objTable.Cell(1, 1).Range.Paste();
This works, but will copy/pasting up to a few thousand strings using the clipboard break anything?
You will need the OpenXML SDK in order to work with OpenXML. It can be quite tricky getting into, but it is very powerful, and a whole lot more stable and reliable than Office Automation or Interop.
The following will open a document, create an AltChunk part, add the HTML to it, and embed it into the document. For a broader overview of AltChunk see Eric White's blog
using (var wordDoc = WordprocessingDocument.Open("DocumentName.docx", true))
{
var altChunkId = "AltChunkId1";
var mainPart = wordDoc.MainDocumentPart;
var chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html, altChunkId);
using (var textStream = new MemoryStream())
{
var html = "<html><body>...</body></html>";
var data = Encoding.UTF8.GetBytes(html);
textStream.Write(data, 0, data.Length);
textStream.Position = 0;
chunk.FeedData(textStream);
}
var altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document.Body.InsertAt(altChunk, 0);
mainPart.Document.Save();
}
Obviously for your case, you will want to find (or build) the table you want and insert the AltChunk there instead of at the first position in the body. Note that the HTML that you insert into the word doc must be full HTML documents, with an <html> tag. I'm not sure if <body> is required, but it doesn't hurt. If you just have HTML formatted text, simply wrap the text in these tags and insert into the doc.
It seems that you will need to use Office Automation/Interop to get the table heights. See this answer which says that the OpenXML SDK does not update the heights, only Word does.
Use this code it is working..
Response.AppendHeader("content-disposition", "attachment;filename=FileEName.xls");
Response.Charset = "";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-excel";
this.EnableViewState = false;
//Response.Write("Your HTML Code");
Response.Write("<table border='1 px solid'><tr><th>sfsd</th><th>sfsdfssd</th></tr><tr>
<td>ssfsdf</td><td><table border='1 px solid'><tr><th>sdf</th><th>hhsdf</th></tr><tr>
<td>sdfds</td><td>sdhjhfds</td></tr></table></td></tr></table>");
Response.End();
Why not let WORD do its owns translation since it understands HTML.
Read your Excel cells
Write your values into a HTML textfile as it would be a WORD document.
Open WORD and let it read that HTML file.
Instruct WORD to save the document as a new WORD document (if that is required).

Irregular character/text encoding issue with writing back to file

I'm using this function to read text lines from a file:
string[] postFileLines = System.IO.File.ReadAllLines(pstPathTextBox.Text);
Inserting a few additional lines at strategic spots, then writing the text lines back to a file with:
TextWriter textW = new StreamWriter(filePath);
for (int i = 0; i < linesToWrite.Count; i++)
{
textW.WriteLine(linesToWrite[i]);
}
textW.Close();
This works perfectly well until the text file I am reading in contains an international or special character. When writing back to the file, I don't get the same character - it is a box.
Ex:
Before = W:\Contrat à faire aujourdhui\ `
After = W:\Contrat � faire aujourdhui\ `
This webpage is portraying it as a question mark, but in the text file it's a rect white box.
Is there a way to include the correct encoding in my application to be able to handle these characters? Or, if not, throw a warning saying it was not able to properly write given line?
Add encondig like this:
File.ReadAllLines(path, Encoding.UTF8);
and
new StreamWriter(filePath, Encoding.UTF8);
Hope it helps.
use This , works for me
string txt = System.IO.File.ReadAllText(inpPath, Encoding.GetEncoding("iso-8859-1"));
You can try UTF encoding while writing to the file as well,
textW.WriteLine(linesToWrite[i],Encoding.UTF8);
You may be need to write Single-byte Character Sets
Using Encoding.GetEncodings() you can easily get all possible encoding. ("DOS" encoding are System.Text.SBCSCodePageEncoding)
In your case you may need to use
File.ReadAllLines(path, Encoding.GetEncoding("IBM850"));
and
new StreamWriter(filePath, Encoding.GetEncoding("IBM850"));
Bonne journée! ;)

YASR - Yet another search and replace question

Environment: asp.net c# openxml
Ok, so I've been reading a ton of snippets and trying to recreate the wheel, but I'm hoping that somone can help me get to my desination faster. I have multiple documents that I need to merge together... check... I'm able to do that with openxml sdk. Birds are singing, sun is shining so far. Now that I have the document the way I want it, I need to search and replace text and/or content controls.
I've tried using my own text - {replace this} but when I look at the xml (rename docx to zip and view the file), the { is nowhere near the text. So I either need to know how to protect that within the doucment so they don't diverge or I need to find another way to search and replace.
I'm able to search/replace if it is an xml file, but then I'm back to not being able to combine the doucments easily.
Code below... and as I mentioned... document merge works fine... just need to replace stuff.
* Update * changed my replace call to go after the tag instead of regex. I have the right info now, but the .Replace call doesn't seem to want to work. Last four lines are for validation that I was seeing the right tag contents. I simply want to replace those contents now.
protected void exeProcessTheDoc(object sender, EventArgs e)
{
string doc1 = Server.MapPath("~/Templates/doc1.docx");
string doc2 = Server.MapPath("~/Templates/doc2.docx");
string final_doc = Server.MapPath("~/Templates/extFinal.docx");
File.Delete(final_doc);
File.Copy(doc1, final_doc);
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(final_doc, true))
{
string altChunkId = "AltChunkId2";
MainDocumentPart mainPart = myDoc.MainDocumentPart;
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open(doc2, FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Elements<Paragraph>().Last());
mainPart.Document.Save();
}
exeSearchReplace(final_doc);
}
public static void GetPropertyFromDocument(string document, string outdoc)
{
XmlDocument xmlProperties = new XmlDocument();
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, false))
{
ExtendedFilePropertiesPart appPart = wordDoc.ExtendedFilePropertiesPart;
xmlProperties.Load(appPart.GetStream());
}
XmlNodeList chars = xmlProperties.GetElementsByTagName("Company");
chars.Item(0).InnerText.Replace("{ClientName}", "Penn Inc.");
StreamWriter sw;
sw = File.CreateText(outdoc);
sw.WriteLine(chars.Item(0).InnerText);
sw.Close();
}
}
}
If I'm reading this right, you have something like "{replace me}" in a .docx and then when you loop through the XML, you're finding things like <t>{replace</t><t> me</><t>}</t> or some such havoc. Now, with XML like that, it's impossible to create a routine that will replace "{replace me}".
If that's the case, then it's very, very likely related to the fact that it's considered a proofing error. i.e. it's misspelled as far as Word is concerned. The cause of it is that you've opened the document in Word and have proofing turned on. As such, the text is marked as "isDirty" and split up into different runs.
The two ways about fixing this are:
Client-side. In Word, just make sure all proofing errors are either corrected or ignored.
Format-side. Use the MarkupSimplifier tool that is part of Open XML Package Editor Power Tool for Visual Studio 2010 to fix this outside of the client. Eric White has a great (and timely for you - just a few days old) write up here on it: Getting Started with Open XML PowerTools Markup Simplifier
If you want to search and replace text in a WordprocessingML document, there is a fairly easy algorithm that you can use:
Break all runs into runs of a single character. This includes runs that have special characters such as a line break, carriage return, or hard tab.
It is then pretty easy to find a set of runs that match the characters in your search string.
Once you have identified a set of runs that match, then you can replace that set of runs with a newly created run (which has the run properties of the run containing the first character that matched the search string).
After replacing the single-character runs with a newly created run, you can then consolidate adjacent runs with identical formatting.
I've written a blog post and recorded a screen-cast that walks through this algorithm.
Blog post: http://openxmldeveloper.org/archive/2011/05/12/148357.aspx
Screen cast: http://www.youtube.com/watch?v=w128hJUu3GM
-Eric

Categories