// // Writer.cs // // Author: Kees van Spelde // // Copyright (c) 2013-2018 Magic-Sessions. (www.magic-sessions.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // using System; using System.Globalization; using System.IO; using System.Text; namespace MsgReader.Rtf { /// /// Rtw writer, use it to write a new Rtf document /// internal sealed class Writer : IDisposable { #region Consts /// /// Hex characters /// private const string Hexs = "0123456789abcdef"; #endregion #region Fields /// /// Inner text writer /// private TextWriter _textWriter; /// /// Text encoding /// // ReSharper disable once UnusedMember.Local private Encoding _unicode = Encoding.Unicode; /// /// current line head /// // ReSharper disable once NotAccessedField.Local private int _lineHead; /// /// Current position /// private int _position; #endregion #region Properties public Encoding Encoding { get; set; } /// /// Whether output rtf code with indent style /// /// /// In rtf formation , recommend do not use indent . this option just to debugger , /// in software development , use this option can genereate indented rtf code friendly to read, /// but after debug , recommend clear this option and set this attribute = false. /// public bool Indent { get; set; } /// /// String used to indent /// public string IndentString { get; set; } /// /// Level of nested groups /// public int GroupLevel { get; private set; } #endregion #region RTFWriter /// /// Initialize instance /// /// text writer public Writer(TextWriter textWriter) { IndentString = " "; Encoding = Encoding.Default; _textWriter = textWriter; } /// /// Initialize instance /// /// File name public Writer(string fileName) { IndentString = " "; Encoding = Encoding.Default; _textWriter = new StreamWriter(fileName, false, Encoding.ASCII); } #endregion #region Dispose public void Dispose() { Close(); } #endregion #region Close public void Close() { if (GroupLevel > 0) throw new Exception("One or more groups did not finish"); if (_textWriter != null) { _textWriter.Close(); _textWriter = null; } } #endregion #region Flush public void Flush() { if (_textWriter != null) _textWriter.Flush(); } #endregion #region WriteGroup /// /// Write completed group wich one keyword /// /// keyword public void WriteGroup(string keyWord) { WriteStartGroup(); WriteKeyword(keyWord); WriteEndGroup(); } /// /// Begin write group /// public void WriteStartGroup() { if (Indent) { InnerWriteNewLine(); _textWriter.Write("{"); } else { _textWriter.Write("{"); } GroupLevel ++; } /// /// End write group /// public void WriteEndGroup() { GroupLevel--; if (GroupLevel < 0) throw new Exception("Group level error"); if (Indent) { InnerWriteNewLine(); InnerWrite("}"); } else InnerWrite("}"); } #endregion #region WriteRaw /// /// Write raw text /// /// text public void WriteRaw(string text) { if (!string.IsNullOrEmpty(text)) InnerWrite(text); } #endregion #region WriteKeyWord /// /// Write keyword /// /// keyword public void WriteKeyword(string keyword) { WriteKeyword(keyword, false); } /// /// Write keyword /// /// keyword /// True if it is an external keyword public void WriteKeyword(string keyword, bool external) { if (string.IsNullOrEmpty(keyword)) throw new ArgumentNullException(nameof(keyword)); if (Indent == false && (keyword == "par" || keyword == "pard")) InnerWrite(Environment.NewLine); if (Indent) { if (keyword == "par" || keyword == "pard") InnerWriteNewLine(); } InnerWrite(external ? "\\*\\" : "\\"); InnerWrite(keyword); } #endregion #region WriteText /// /// Write plain text /// /// Text public void WriteText(string text) { if (string.IsNullOrEmpty(text)) return; WriteText(text, true); } public void WriteUnicodeText(string text) { if (string.IsNullOrEmpty(text) == false) { WriteKeyword("uc1"); foreach (var c in text) { if (c > 127) { int v = c; var v2 = (short) v; WriteKeyword("u" + v2); WriteRaw(" ?"); } else { InnerWriteChar(c); } } } } /// /// Write plain text, with the option to automaticly add a white space character /// /// Text /// Write a white space automatic public void WriteText(string text, bool autoAddWhitespace) { if (string.IsNullOrEmpty(text)) return; if (autoAddWhitespace) InnerWrite(' '); foreach (var c in text) InnerWriteChar(c); } private void InnerWriteChar(char c) { if (c == '\t') { WriteKeyword("tab"); InnerWrite(' '); } if (c > 32 && c < 127) { // Some special characters , must be converted if (c == '\\' || c == '{' || c == '}') InnerWrite('\\'); InnerWrite(c); } else { var bytes = Encoding.GetBytes(c.ToString(CultureInfo.InvariantCulture)); foreach (var b in bytes) { InnerWrite("\\\'"); WriteByte(b); } } } #endregion #region WriteBytes /// /// Write binary data /// /// Binary data public void WriteBytes(byte[] bytes) { if (bytes == null || bytes.Length == 0) return; WriteRaw(" "); for (var count = 0; count < bytes.Length; count ++) { if ((count%32) == 0) { WriteRaw(Environment.NewLine); WriteIndent(); } else if ((count%8) == 0) { WriteRaw(" "); } var b = bytes[count]; var h = (b & 0xf0) >> 4; var l = b & 0xf; _textWriter.Write(Hexs[h]); _textWriter.Write(Hexs[l]); _position += 2; } } /// /// Write a byte /// /// byte public void WriteByte(byte b) { var h = (b & 0xf0) >> 4; var l = b & 0xf; _textWriter.Write(Hexs[h]); _textWriter.Write(Hexs[l]); _position += 2; } #endregion #region Internal functions private void InnerWrite(char c) { _position ++; _textWriter.Write(c); } private void InnerWrite(string text) { _position += text.Length; _textWriter.Write(text); } private void InnerWriteNewLine() { if (Indent) { if (_position > 0) { InnerWrite(Environment.NewLine); _lineHead = _position; WriteIndent(); } } } private void WriteIndent() { if (Indent) { for (var count = 0; count < GroupLevel; count ++) { InnerWrite(IndentString); } } } #endregion } }