Search code examples
c#.netencodingstreamrtf

Edit RTF as plain text but unable to open it again


I have a RTF file that I want to open, replace a String "TEMPLATE_Name" and save. But after saving, the file cannot open correctly again. When I use MS Word, the file opens and shows the RTF raw code instead the text.

I am afraid I am breaking the format or the encoding but I don't really know how:

        using (MemoryStream ms = new MemoryStream(1000))
        using (StreamWriter sw = new StreamWriter(ms,Encoding.UTF8))
        {
            using (Stream fsSource = new FileStream(Server.MapPath("~/LetterTemplates/TestTemplate.rtf"), FileMode.Open))
            using (StreamReader sr = new StreamReader(fsSource,Encoding.UTF8))
                while (!sr.EndOfStream)
                {
                    String line = sr.ReadLine();
                    line = line.Replace("TEMPLATE_Name", model.FirstName + " " + model.LastName);
                    sw.WriteLine(line);
                }

            ms.Position = 0;

            using (FileStream fs = new FileStream(Server.MapPath("~/LetterTemplates/test.rtf"), FileMode.Create))
                ms.CopyTo(fs);
        }

Any idea about what could be the issue?

Thanks.

SOLUTION: One problem was what @BrokenGlass has pointed out, the fact I was not flushing the stream. The other was the encoding. In the fist line of the RTF file I can see:

{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\

So, even without understand anything about RTF, I set the encoding to code page 1252 and it works:

        using (MemoryStream ms = new MemoryStream(1000))
        using (StreamWriter sw = new StreamWriter(ms,Encoding.GetEncoding(1252)))
        {
            using (Stream fsSource = new FileStream(Server.MapPath("~/LetterTemplates/TestTemplate.rtf"), FileMode.Open))
            using (StreamReader sr = new StreamReader(fsSource,Encoding.GetEncoding(1252)))
                while (!sr.EndOfStream)
                {
                    String line = sr.ReadLine();
                    line = line.Replace("TEMPLATE_Name", model.FirstName + " " + model.LastName);
                    sw.WriteLine(line);
                }

            sw.Flush();
            ms.Position = 0;

            using (FileStream fs = new FileStream(Server.MapPath("~/LetterTemplates/test.rtf"), FileMode.Create))
                ms.CopyTo(fs);
        }

Solution

  • StreamWriter is buffering content - make sure you call sw.Flush() before reading from your memory stream.

    StreamWriter.Flush():

    Clears all buffers for the current writer and causes any buffered data to be written to the underlying stream.

    Edit in light of comments:

    A better alternative as @leppie alluded to is restructuring the code to use the using block to force flushing, instead of explicitly doing it:

     using (MemoryStream ms = new MemoryStream(1000))
     {
       using (StreamWriter sw = new StreamWriter(ms,Encoding.UTF8))
       {
         //...
       }
       ms.Position = 0;
       //Write to file
     }
    

    An even better alternative as @Slaks pointed out is writing to the file directly and not using a memory stream at all - unless there are other reasons you are doing this this seems to be the most straightforward solution, it would simplify your code and avoid buffering the file in memory.