zaaReloaded2/zaaReloaded2/Formatter/DocumentWriter.cs
2015-09-06 11:48:43 +02:00

303 lines
9.1 KiB
C#
Executable File

/* DocumentWriter.cs
* part of zaaReloaded2
*
* Copyright 2015 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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// <para>
/// Linking several DocumentWriters permits a cascading work flow
/// with several buffers.
/// </para>
/// <para>
/// Markup support: The DocumentWriter supports basic markup to control
/// the text styles in the Word document.
/// </para>
/// <list type="unordered">
/// <item>&lt;b&gt; and &lt;/b&gt; - bold/unbold</item>
/// <item>&lt;style:NAME&gt; - set the paragraph or character style</item>
/// <item>&lt;/style&gt; - remove *character* style</item>
/// </list>
/// </remarks>
class DocumentWriter
{
#region Properties
/// <summary>
/// Gets the Document associated with this DocumentWriter,
/// or null if there is no associated Document.
/// </summary>
public Document Document { get; set; }
/// <summary>
/// Gets the parent DocumentWriter (if any).
/// </summary>
public DocumentWriter Parent { get; set; }
/// <summary>
/// Returns true if there is text in the buffer.
/// </summary>
public bool HasBufferedText { get { return _buffer.Length > 0; } }
/// <summary>
/// Returns text without markup from the buffer.
/// </summary>
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();
}
/// <summary>
/// Creates a new DocumentWriter instance that is associated
/// with a Word Document.
/// </summary>
/// <param name="document"></param>
public DocumentWriter(Document document)
: this()
{
Document = document;
}
/// <summary>
/// Creates a new DocumentWriter instance that is associated
/// with a parent DocumentWriter.
/// </summary>
/// <param name="parent"></param>
public DocumentWriter(DocumentWriter parent)
: this()
{
Parent = parent;
}
#endregion
#region Overrides
public override string ToString()
{
return _buffer.ToString();
}
#endregion
#region Public methods
/// <summary>
/// Flushes the buffer to the associated Word document and/or the
/// parent DocumentWriter.
/// </summary>
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();
}
/// <summary>
/// Writes text to the buffer.
/// </summary>
/// <param name="text">Text to write to the buffer.</param>
public void Write(string text)
{
_buffer.Append(text);
}
/// <summary>
/// Appends a line of text to the buffer.
/// </summary>
/// <param name="text">Text to append.</param>
public void WriteLine(string text)
{
_buffer.AppendLine(text);
}
/// <summary>
/// Inserts text at the start of the buffer.
/// </summary>
/// <param name="text">Text to insert at the start of the
/// buffer.</param>
public void Prepend(string text)
{
_buffer.Insert(0, text);
}
#endregion
#region Private methods
/// <summary>
/// Parses a string containing markup (e.g., "&lt;b&gt;", "&lt;/b&gt;")
/// and writes formatted text to the current Document.
/// </summary>
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 "<b>":
sel.Font.Bold = 1;
break;
case "</b>":
sel.Font.Bold = 0;
break;
case "<i>":
sel.Font.Italic = 1;
break;
case "</i>":
sel.Font.Italic = 0;
break;
case "<u>":
sel.Font.Underline = WdUnderline.wdUnderlineSingle;
break;
case "</u>":
sel.Font.Underline = WdUnderline.wdUnderlineNone;
break;
case "<highlight>":
hightlights.Start(sel.Range);
break;
case "</highlight>":
hightlights.Stop(sel.Range);
break;
case "</style>":
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(@"<style:(?<style>[^>]+)>");
static Range _highlightStart;
#endregion
#region Helper class for highlighting
/// <summary>
/// Embedded helper class to manage highlights.
/// </summary>
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<Range>();
_currentHighlight = null;
}
Collection<Range> _highlights;
Range _currentHighlight;
}
#endregion
}
}