/* DocumentWriter.cs * part of zaaReloaded2 * * Copyright 2015-2017 Daniel Kraus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Office.Interop.Word; using System.Text.RegularExpressions; using System.Collections.ObjectModel; namespace zaaReloaded2.Formatter { /// /// Helper class that serves to write text to a Word document or /// to a linked DocumentWriter. Provides a buffer that can be /// appended or prepended to, which facilitates conditional output /// depending on whether there is text in the buffer or not. /// /// /// /// Linking several DocumentWriters permits a cascading work flow /// with several buffers. /// /// /// Markup support: The DocumentWriter supports basic markup to control /// the text styles in the Word document. /// /// /// <b> and </b> - bold/unbold /// <style:NAME> - set the paragraph or character style /// </style> - remove *character* style /// /// class DocumentWriter { #region Properties /// /// Gets the Document associated with this DocumentWriter, /// or null if there is no associated Document. /// public Document Document { get; set; } /// /// Gets the parent DocumentWriter (if any). /// public DocumentWriter Parent { get; set; } /// /// Returns true if there is text in the buffer. /// public bool HasBufferedText { get { return _buffer.Length > 0; } } /// /// Returns text without markup from the buffer. /// public string Text { get { if (!HasBufferedText) throw new InvalidOperationException("This DocumentWriter does not have any text."); return _markupRegex.Replace(_buffer.ToString(), String.Empty); } } #endregion #region Constructors public DocumentWriter() { _buffer = new StringBuilder(); } /// /// Creates a new DocumentWriter instance that is associated /// with a Word Document. /// /// public DocumentWriter(Document document) : this() { Document = document; } /// /// Creates a new DocumentWriter instance that is associated /// with a parent DocumentWriter. /// /// public DocumentWriter(DocumentWriter parent) : this() { Parent = parent; } #endregion #region Overrides public override string ToString() { return _buffer.ToString(); } #endregion #region Public methods /// /// Flushes the buffer to the associated Word document and/or the /// parent DocumentWriter. /// public void Flush() { if (!HasBufferedText) return; if (Document == null && Parent == null) { throw new InvalidOperationException( "No document and no parent buffer to flush into."); } if (Document != null) { Selection s = Document.ActiveWindow.Selection; s.ClearCharacterDirectFormatting(); s.ClearParagraphDirectFormatting(); MarkupToDocument(_buffer.ToString()); } if (Parent != null) { Parent.Write(_buffer.ToString()); } _buffer.Clear(); } /// /// Writes text to the buffer. /// /// Text to write to the buffer. public void Write(string text) { _buffer.Append(text); } /// /// Appends a line of text to the buffer. /// /// Text to append. public void WriteLine(string text) { _buffer.AppendLine(text); } /// /// Appends a newline to the buffer. /// public void WriteLine() { _buffer.AppendLine(); } /// /// Inserts text at the start of the buffer. /// /// Text to insert at the start of the /// buffer. public void Prepend(string text) { _buffer.Insert(0, text); } #endregion #region Private methods /// /// Parses a string containing markup (e.g., "<b>", "</b>") /// and writes formatted text to the current Document. /// void MarkupToDocument(string text) { string[] substrings = _markupRegex.Split(text); Selection sel = Document.ActiveWindow.Selection; Highlights hightlights = new Highlights(); foreach (string substring in substrings) { switch (substring) { case "": sel.Font.Bold = 1; break; case "": sel.Font.Bold = 0; break; case "": sel.Font.Italic = 1; break; case "": sel.Font.Italic = 0; break; case "": sel.Font.Underline = WdUnderline.wdUnderlineSingle; break; case "": sel.Font.Underline = WdUnderline.wdUnderlineNone; break; case "": hightlights.Start(sel.Range); break; case "": hightlights.Stop(sel.Range); break; case "": sel.ClearCharacterStyle(); break; default: Match styleMatch = _styleRegex.Match(substring); if (styleMatch.Success) { sel.set_Style(styleMatch.Groups["style"].Value); } else { sel.TypeText(substring); } break; } } hightlights.ApplyHighlights(); } #endregion #region Fields StringBuilder _buffer; // Put pattern in parentheses so they will not be discarded by Regex.Split // The splitting pattern must not contain subgroups! static readonly Regex _markupRegex = new Regex(@"(<[^ >]+>)"); static readonly Regex _styleRegex = new Regex(@"[^>]+)>"); #endregion #region Helper class for highlighting /// /// Embedded helper class to manage highlights. /// class Highlights { public void Start(Range start) { if (_currentHighlight == null) { _currentHighlight = start; _highlights.Add(_currentHighlight); } } public void Stop(Range stop) { if (_currentHighlight != null) { _currentHighlight.End = stop.End; _currentHighlight = null; } } public void ApplyHighlights() { foreach (Range r in _highlights) { r.HighlightColorIndex = WdColorIndex.wdYellow; } } public Highlights() { _highlights = new Collection(); _currentHighlight = null; } Collection _highlights; Range _currentHighlight; } #endregion } }