303 lines
9.1 KiB
C#
Executable File
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><b> and </b> - bold/unbold</item>
|
|
/// <item><style:NAME> - set the paragraph or character style</item>
|
|
/// <item></style> - 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., "<b>", "</b>")
|
|
/// 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
|
|
}
|
|
}
|