// // Reader.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.Collections.Generic; using System.IO; using System.Text; namespace MsgReader.Rtf { /// /// Rtf reader /// internal sealed class Reader : IDisposable { #region Fields private readonly Stack _layerStack = new Stack(); private bool _firstTokenInGroup; private Stream _stream; private Lex _lex; #endregion #region Properties public TextReader InnerReader { get; private set; } /// /// Current token /// public Token CurrentToken { get; private set; } /// /// The /// public DocumentFormatInfo Format { get; internal set; } /// /// Current token's type /// public RtfTokenType TokenType => CurrentToken?.Type ?? RtfTokenType.None; /// /// Current keyword /// public string Keyword => CurrentToken?.Key; /// /// If current token has a parameter /// public bool HasParam => CurrentToken != null && CurrentToken.HasParam; /// /// Current parameter /// public int Parameter => CurrentToken?.Param ?? 0; public int ContentPosition { get { if (_stream == null) return 0; return (int) _stream.Position; } } public int ContentLength { get { if (_stream == null) return 0; return (int)_stream.Length; } } /// /// Current token is the first token in owner group /// public bool FirstTokenInGroup => _firstTokenInGroup; /// /// Lost token /// public Token LastToken { get; private set; } public int Level { get; private set; } /// /// Total of this object handle tokens /// public int TokenCount { get; set; } internal bool EnableDefaultProcess { get; set; } /// /// When set to true then we are parsing an RTF unicode /// high - low surrogate /// internal bool ParsingHighLowSurrogate { get; set; } /// /// When is set to true /// then this will containt the high surrogate value when we are /// parsing the low surrogate value /// internal int? HighSurrogateValue { get; set; } /// /// /// public LayerInfo CurrentLayerInfo { get { if (_layerStack.Count == 0) _layerStack.Push(new LayerInfo()); return _layerStack.Peek(); } } #endregion #region Constructors /// /// Initialize instance /// public Reader() { EnableDefaultProcess = true; } /// /// Initialize instance from file /// // ReSharper disable once UnusedMember.Global public Reader(string fileName) { EnableDefaultProcess = true; LoadRtfFile(fileName); } /// /// Initialize instance from stream /// public Reader(Stream stream) { EnableDefaultProcess = true; var reader = new StreamReader(stream, Encoding.ASCII); LoadReader(reader); _stream = stream; } /// /// Initialize instance from text reader /// public Reader(TextReader reader) { EnableDefaultProcess = true; LoadReader(reader); } #endregion #region Displose /// /// Dispose this object /// public void Dispose() { Close(); } #endregion #region Load /// /// Load rtf from file /// /// spcial file name /// is operation successful public bool LoadRtfFile(string fileName) { CurrentToken = null; if (File.Exists(fileName)) { var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); InnerReader = new StreamReader(stream, Encoding.ASCII); _stream = stream; _lex = new Lex(InnerReader); return true; } return false; } /// /// Load rtf from reader /// /// text reader /// is operation successful public void LoadReader(TextReader reader) { //.Clear(); CurrentToken = null; InnerReader = reader; _lex = new Lex(InnerReader); } /// /// Load rtf from string /// /// RTF text /// is operation successful public bool LoadRtfText(string text) { //myTokenStack.Clear(); CurrentToken = null; if (text != null && text.Length > 3) { InnerReader = new StringReader(text); _lex = new Lex(InnerReader); return true; } return false; } #endregion #region Close /// /// Close the inner reader /// public void Close() { if (InnerReader != null) { InnerReader.Close(); InnerReader = null; } } #endregion #region PeekTokenType /// /// Get next token type /// /// public RtfTokenType PeekTokenType() { return _lex.PeekTokenType(); } #endregion #region DefaultProcess public void DefaultProcess() { if (CurrentToken == null) return; switch (CurrentToken.Key) { case "uc": CurrentLayerInfo.UcValue = Parameter; break; case "u": if (InnerReader.Peek() == '?') InnerReader.Read(); break; } } #endregion #region ReadToken /// /// Read token /// /// token read public Token ReadToken() { _firstTokenInGroup = false; LastToken = CurrentToken; if (LastToken != null && LastToken.Type == RtfTokenType.GroupStart) _firstTokenInGroup = true; CurrentToken = _lex.NextToken(); if (CurrentToken == null || CurrentToken.Type == RtfTokenType.Eof) { CurrentToken = null; return null; } TokenCount++; if (CurrentToken.Type == RtfTokenType.GroupStart) { if (_layerStack.Count == 0) _layerStack.Push(new LayerInfo()); else { var info = _layerStack.Peek(); _layerStack.Push(info.Clone()); } Level++; } else if (CurrentToken.Type == RtfTokenType.GroupEnd) { if (_layerStack.Count > 0) _layerStack.Pop(); Level--; } if (EnableDefaultProcess) DefaultProcess(); return CurrentToken; } #endregion #region ReadToEndOfGroup /// /// Read and ignore data , until just the end of the current group, preserve the end. /// public void ReadToEndOfGroup() { var level = 0; while (true) { var c = InnerReader.Peek(); if (c == -1) { break; } if (c == '{') { level++; } else if (c == '}') { level--; if (level < 0) { break; } } InnerReader.Read(); } } #endregion #region ToString public override string ToString() { return "RTFReader Level:" + Level + " " + Keyword; } #endregion } }