ASCU_ALL/MigraDoc.Rendering/Rendering/ParagraphIterator.cs

287 lines
10 KiB
C#
Raw Normal View History

2020-09-04 12:49:15 +05:00
#region MigraDoc - Creating Documents on the Fly
//
// Authors:
// Klaus Potzesny
//
// Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://www.migradoc.com
// http://sourceforge.net/projects/pdfsharp
//
// 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 NONINFRINGEMENT. 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.
#endregion
using System.Collections.Generic;
using MigraDoc.DocumentObjectModel;
namespace MigraDoc.Rendering
{
/// <summary>
/// Iterates sequentially through the elements of a paragraph.
/// </summary>
public class ParagraphIterator
{
/// <summary>
/// Initializes a paragraph iterator pointing on the given paragraph elements object.
/// Paragraph iterators received from this paragraph iterator relate to this root node.
/// </summary>
/// <param name="rootNode">The root node for the paragraph iterator.</param>
public ParagraphIterator(ParagraphElements rootNode)
{
_rootNode = rootNode;
_current = rootNode;
_positionIndices = new List<int>();
}
/// <summary>
/// Initializes a paragraph iterator given the root node, its position in the object tree and the current object
/// </summary>
/// <param name="rootNode">The node the position indices relate to.</param>
/// <param name="current">The element the iterator shall point to.</param>
/// <param name="indices">The position of the paragraph iterator in terms of element indices.</param>
private ParagraphIterator(ParagraphElements rootNode, DocumentObject current, List<int> indices)
{
_rootNode = rootNode;
_positionIndices = indices;
_current = current;
}
/// <summary>
/// Determines whether this iterator is the first leaf of the root node.
/// </summary>
public bool IsFirstLeaf
{
get
{
if (!(_current is DocumentElements))
{
ParagraphIterator prevIter = GetPreviousLeaf();
return prevIter == null;
}
return false;
}
}
/// <summary>
/// Determines whether this iterator is the last leaf of the document object tree.
/// </summary>
public bool IsLastLeaf
{
get
{
if (!(_current is DocumentElements))
{
ParagraphIterator nextIter = GetNextLeaf();
return nextIter == null;
}
return false;
}
}
/// <summary>
/// Gets the document object this instance ponits to.
/// </summary>
public DocumentObject Current
{
get { return _current; }
}
/// <summary>
/// Gets the last leaf of the document object tree.
/// </summary>
/// <returns>The paragraph iterator pointing to the last leaf in the document object tree.</returns>
public ParagraphIterator GetLastLeaf()
{
if (_rootNode.Count == 0)
return null;
return SeekLastLeaf();
}
/// <summary>
/// Gets the first leaf of the element tree.
/// </summary>
/// <returns>The paragraph iterator pointing to the first leaf in the element tree.</returns>
public ParagraphIterator GetFirstLeaf()
{
if (_rootNode.Count == 0)
return null;
return SeekFirstLeaf();
}
/// <summary>
/// Returns the next iterator in the tree pointing to a leaf.
/// </summary>
/// <remarks>This function is intended to receive the renderable objects of a paragraph.
/// Thus, empty ParagraphElement objects (which are collections) don't count as leafs.</remarks>
public ParagraphIterator GetNextLeaf()
{
//Move up to appropriate parent element
ParagraphIterator parIterator = GetParentIterator();
if (parIterator == null)
return null;
int elementIndex = LastIndex;
ParagraphElements parEls = (ParagraphElements)parIterator._current;
while (elementIndex == parEls.Count - 1)
{
elementIndex = parIterator.LastIndex;
parIterator = parIterator.GetParentIterator();
if (parIterator == null)
break;
parEls = (ParagraphElements)parIterator._current;
}
if (parIterator == null)
return null;
int newIndex = elementIndex + 1;
if (newIndex >= parEls.Count)
return null;
List<int> indices = new List<int>(parIterator._positionIndices); //(Array_List)parIterator.positionIndices.Clone();
indices.Add(newIndex);
DocumentObject obj = GetNodeObject(parEls[newIndex]);
ParagraphIterator iterator = new ParagraphIterator(_rootNode, obj, indices);
return iterator.SeekFirstLeaf();
}
/// <summary>
/// Gets the object a paragraph iterator shall point to.
/// Only ParagraphElements and renderable objects are allowed.
/// </summary>
/// <param name="obj">The object to select the node object for.</param>
/// <returns>The object a paragraph iterator shall point to.</returns>
private DocumentObject GetNodeObject(DocumentObject obj)
{
if (obj is FormattedText)
return ((FormattedText)obj).Elements;
if (obj is Hyperlink)
return ((Hyperlink)obj).Elements;
return obj;
}
/// <summary>
/// Returns the previous iterator to a leaf in the document object tree pointing.
/// </summary>
/// <returns>The previous leaf, null if none exists.</returns>
public ParagraphIterator GetPreviousLeaf()
{
//Move up to appropriate parent element
ParagraphIterator parIterator = GetParentIterator();
if (parIterator == null)
return null;
int elementIndex = LastIndex;
ParagraphElements parEls = (ParagraphElements)parIterator._current;
while (elementIndex == 0)
{
elementIndex = parIterator.LastIndex;
parIterator = parIterator.GetParentIterator();
if (parIterator == null)
break;
parEls = (ParagraphElements)parIterator._current;
}
if (parIterator == null)
return null;
int newIndex = elementIndex - 1;
if (newIndex < 0)
return null;
List<int> indices = new List<int>(parIterator._positionIndices);//(Array_List)parIterator.positionIndices.Clone();
indices.Add(newIndex);
DocumentObject obj = GetNodeObject(parEls[newIndex]);
ParagraphIterator iterator = new ParagraphIterator(_rootNode, obj, indices);
return iterator.SeekLastLeaf();
}
private ParagraphIterator SeekLastLeaf()
{
DocumentObject obj = Current;
if (!(obj is ParagraphElements))
return this;
List<int> indices = new List<int>(_positionIndices);
while (obj is ParagraphElements)
{
ParagraphElements parEls = (ParagraphElements)obj;
if (((ParagraphElements)obj).Count == 0)
return new ParagraphIterator(_rootNode, obj, indices);
int idx = ((ParagraphElements)obj).Count - 1;
indices.Add(idx);
obj = GetNodeObject(parEls[idx]);
}
return new ParagraphIterator(_rootNode, obj, indices);
}
/// <summary>
/// Gets the leftmost leaf within the hierarchy.
/// </summary>
/// <returns>The searched leaf.</returns>
ParagraphIterator SeekFirstLeaf()
{
DocumentObject obj = Current;
if (!(obj is ParagraphElements))
return this;
List<int> indices = new List<int>(_positionIndices);
while (obj is ParagraphElements)
{
ParagraphElements parEls = (ParagraphElements)obj;
if (parEls.Count == 0)
return new ParagraphIterator(_rootNode, obj, indices);
indices.Add(0);
obj = GetNodeObject(parEls[0]);
}
return new ParagraphIterator(_rootNode, obj, indices);
}
private ParagraphIterator GetParentIterator()
{
if (_positionIndices.Count == 0)
return null;
List<int> indices = new List<int>(_positionIndices);
indices.RemoveAt(indices.Count - 1);
DocumentObject parent = DocumentRelations.GetParentOfType(_current, typeof(ParagraphElements));
return new ParagraphIterator(_rootNode, parent, indices);
}
private int LastIndex
{
get
{
if (_positionIndices.Count == 0)
return -1;
return _positionIndices[_positionIndices.Count - 1];
}
}
readonly ParagraphElements _rootNode;
readonly List<int> _positionIndices;
readonly DocumentObject _current;
}
}