#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 { /// /// Iterates sequentially through the elements of a paragraph. /// public class ParagraphIterator { /// /// Initializes a paragraph iterator pointing on the given paragraph elements object. /// Paragraph iterators received from this paragraph iterator relate to this root node. /// /// The root node for the paragraph iterator. public ParagraphIterator(ParagraphElements rootNode) { _rootNode = rootNode; _current = rootNode; _positionIndices = new List(); } /// /// Initializes a paragraph iterator given the root node, its position in the object tree and the current object /// /// The node the position indices relate to. /// The element the iterator shall point to. /// The position of the paragraph iterator in terms of element indices. private ParagraphIterator(ParagraphElements rootNode, DocumentObject current, List indices) { _rootNode = rootNode; _positionIndices = indices; _current = current; } /// /// Determines whether this iterator is the first leaf of the root node. /// public bool IsFirstLeaf { get { if (!(_current is DocumentElements)) { ParagraphIterator prevIter = GetPreviousLeaf(); return prevIter == null; } return false; } } /// /// Determines whether this iterator is the last leaf of the document object tree. /// public bool IsLastLeaf { get { if (!(_current is DocumentElements)) { ParagraphIterator nextIter = GetNextLeaf(); return nextIter == null; } return false; } } /// /// Gets the document object this instance ponits to. /// public DocumentObject Current { get { return _current; } } /// /// Gets the last leaf of the document object tree. /// /// The paragraph iterator pointing to the last leaf in the document object tree. public ParagraphIterator GetLastLeaf() { if (_rootNode.Count == 0) return null; return SeekLastLeaf(); } /// /// Gets the first leaf of the element tree. /// /// The paragraph iterator pointing to the first leaf in the element tree. public ParagraphIterator GetFirstLeaf() { if (_rootNode.Count == 0) return null; return SeekFirstLeaf(); } /// /// Returns the next iterator in the tree pointing to a leaf. /// /// This function is intended to receive the renderable objects of a paragraph. /// Thus, empty ParagraphElement objects (which are collections) don't count as leafs. 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 indices = new List(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(); } /// /// Gets the object a paragraph iterator shall point to. /// Only ParagraphElements and renderable objects are allowed. /// /// The object to select the node object for. /// The object a paragraph iterator shall point to. private DocumentObject GetNodeObject(DocumentObject obj) { if (obj is FormattedText) return ((FormattedText)obj).Elements; if (obj is Hyperlink) return ((Hyperlink)obj).Elements; return obj; } /// /// Returns the previous iterator to a leaf in the document object tree pointing. /// /// The previous leaf, null if none exists. 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 indices = new List(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 indices = new List(_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); } /// /// Gets the leftmost leaf within the hierarchy. /// /// The searched leaf. ParagraphIterator SeekFirstLeaf() { DocumentObject obj = Current; if (!(obj is ParagraphElements)) return this; List indices = new List(_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 indices = new List(_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 _positionIndices; readonly DocumentObject _current; } }