#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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using MigraDoc.DocumentObjectModel;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using MigraDoc.DocumentObjectModel.Fields;
using MigraDoc.DocumentObjectModel.Shapes;
using MigraDoc.Rendering.Resources;

namespace MigraDoc.Rendering
{
    public struct TabOffset
    {
        public TabOffset(TabLeader leader, XUnit offset)
        {
            Leader = leader;
            Offset = offset;
        }
        public TabLeader Leader;
        public XUnit Offset;
    }

    /// <summary>
    /// Summary description for ParagraphRenderer.
    /// </summary>
    public class ParagraphRenderer : Renderer
    {
        /// <summary>
        /// Process phases of the renderer.
        /// </summary>
        private enum Phase
        {
            Formatting,
            Rendering
        }

        /// <summary>
        /// Results that can occur when processing a paragraph element
        /// during formatting.
        /// </summary>
        private enum FormatResult
        {
            /// <summary>
            /// Ignore the current element during formatting.
            /// </summary>
            Ignore,

            /// <summary>
            /// Continue with the next element within the same line.
            /// </summary>
            Continue,

            /// <summary>
            /// Start a new line from the current object on.
            /// </summary>
            NewLine,

            /// <summary>
            /// Break formatting and continue in a new area (e.g. a new page).
            /// </summary>
            NewArea
        }
        Phase _phase;

        /// <summary>
        /// Initializes a ParagraphRenderer object for formatting.
        /// </summary>
        /// <param name="gfx">The XGraphics object to do measurements on.</param>
        /// <param name="paragraph">The paragraph to format.</param>
        /// <param name="fieldInfos">The field infos.</param>
        public ParagraphRenderer(XGraphics gfx, Paragraph paragraph, FieldInfos fieldInfos)
            : base(gfx, paragraph, fieldInfos)
        {
            _paragraph = paragraph;

            ParagraphRenderInfo parRenderInfo = new ParagraphRenderInfo();
            parRenderInfo.DocumentObject = _paragraph;
            ((ParagraphFormatInfo)parRenderInfo.FormatInfo)._widowControl = _paragraph.Format.WidowControl;

            _renderInfo = parRenderInfo;
        }

        /// <summary>
        /// Initializes a ParagraphRenderer object for rendering.
        /// </summary>
        /// <param name="gfx">The XGraphics object to render on.</param>
        /// <param name="renderInfo">The render info object containing information necessary for rendering.</param>
        /// <param name="fieldInfos">The field infos.</param>
        public ParagraphRenderer(XGraphics gfx, RenderInfo renderInfo, FieldInfos fieldInfos)
            : base(gfx, renderInfo, fieldInfos)
        {
            _paragraph = (Paragraph)renderInfo.DocumentObject;
        }

        /// <summary>
        /// Renders the paragraph.
        /// </summary>
        public override void Render()
        {
            InitRendering();
            if ((int)_paragraph.Format.OutlineLevel >= 1 && _gfx.PdfPage != null) // Don't call GetOutlineTitle() in vain
                _documentRenderer.AddOutline((int)_paragraph.Format.OutlineLevel, GetOutlineTitle(), _gfx.PdfPage);

            RenderShading();
            RenderBorders();

            ParagraphFormatInfo parFormatInfo = (ParagraphFormatInfo)_renderInfo.FormatInfo;
            for (int idx = 0; idx < parFormatInfo.LineCount; ++idx)
            {
                LineInfo lineInfo = parFormatInfo.GetLineInfo(idx);
                _isLastLine = (idx == parFormatInfo.LineCount - 1);

                _lastTabPosition = 0;
                if (lineInfo.ReMeasureLine)
                    ReMeasureLine(ref lineInfo);

                RenderLine(lineInfo);
            }
        }

        bool IsRenderedField(DocumentObject docObj)
        {
            if (docObj is NumericFieldBase)
                return true;

            if (docObj is DocumentInfo)
                return true;

            if (docObj is DateField)
                return true;

            return false;
        }

        string GetFieldValue(DocumentObject field)
        {
            NumericFieldBase numericFieldBase = field as NumericFieldBase;
            if (numericFieldBase != null)
            {
                int number = -1;
                PageRefField refField = field as PageRefField;
                if (refField != null)
                {
                    PageRefField pageRefField = refField;
                    number = _fieldInfos.GetShownPageNumber(pageRefField.Name);
                    if (number <= 0)
                    {
                        if (_phase == Phase.Formatting)
                            return "XX";
                        return Messages2.BookmarkNotDefined(pageRefField.Name);
                    }
                }
                else if (field is SectionField)
                {
                    number = _fieldInfos.Section;
                    if (number <= 0)
                        return "XX";
                }
                else if (field is PageField)
                {
                    number = _fieldInfos.DisplayPageNr;
                    if (number <= 0)
                        return "XX";
                }
                else if (field is NumPagesField)
                {
                    number = _fieldInfos.NumPages;
                    if (number <= 0)
                        return "XXX";
                }
                else if (field is SectionPagesField)
                {
                    number = _fieldInfos.SectionPages;
                    if (number <= 0)
                        return "XX";
                }
                return NumberFormatter.Format(number, numericFieldBase.Format);
            }
            else
            {
                DateField dateField = field as DateField;
                if (dateField != null)
                {
                    DateTime dt = (_fieldInfos.Date);
                    if (dt == DateTime.MinValue)
                        dt = DateTime.Now;

                    return _fieldInfos.Date.ToString(dateField.Format);
                }

                InfoField infoField = field as InfoField;
                if (infoField != null)
                    return GetDocumentInfo(infoField.Name);

                Debug.Assert(false, "Given parameter must be a rendered Field");
            }

            return "";
        }

        string GetOutlineTitle()
        {
            ParagraphIterator iter = new ParagraphIterator(_paragraph.Elements);
            iter = iter.GetFirstLeaf();

            bool ignoreBlank = true;
            string title = "";
            while (iter != null)
            {
                DocumentObject current = iter.Current;
                if (!ignoreBlank && (IsBlank(current) || IsTab(current) || IsLineBreak(current)))
                {
                    title += " ";
                    ignoreBlank = true;
                }
                else if (current is Text)
                {
                    title += ((Text)current).Content;
                    ignoreBlank = false;
                }
                else if (IsRenderedField(current))
                {
                    title += GetFieldValue(current);
                    ignoreBlank = false;
                }
                else if (IsSymbol(current))
                {
                    title += GetSymbol((Character)current);
                    ignoreBlank = false;
                }

                if (title.Length > 64)
                    break;
                iter = iter.GetNextLeaf();
            }
            return title;
        }

        /// <summary>
        /// Gets a layout info with only margin and break information set.
        /// It can be taken before the paragraph is formatted.
        /// </summary>
        /// <remarks>
        /// The following layout information is set properly:<br />
        /// MarginTop, MarginLeft, MarginRight, MarginBottom, KeepTogether, KeepWithNext, PagebreakBefore.
        /// </remarks>
        public override LayoutInfo InitialLayoutInfo
        {
            get
            {
                LayoutInfo layoutInfo = new LayoutInfo();
                layoutInfo.PageBreakBefore = _paragraph.Format.PageBreakBefore;
                layoutInfo.MarginTop = _paragraph.Format.SpaceBefore.Point;
                layoutInfo.MarginBottom = _paragraph.Format.SpaceAfter.Point;
                //Don't confuse margins with left or right indent.
                //Indents are invisible for the layouter.
                layoutInfo.MarginRight = 0;
                layoutInfo.MarginLeft = 0;
                layoutInfo.KeepTogether = _paragraph.Format.KeepTogether;
                layoutInfo.KeepWithNext = _paragraph.Format.KeepWithNext;
                return layoutInfo;
            }
        }

        /// <summary>
        /// Adjusts the current x position to the given tab stop if possible.
        /// </summary>
        /// <returns>True, if the text doesn't fit the line any more and the tab causes a line break.</returns>
        FormatResult FormatTab()
        {
            // For Tabs in Justified context
            if (_paragraph.Format.Alignment == ParagraphAlignment.Justify)
                _reMeasureLine = true;
            TabStop nextTabStop = GetNextTabStop();
            _savedWordWidth = 0;
            if (nextTabStop == null)
                return FormatResult.NewLine;

            bool notFitting = false;
            XUnit xPositionBeforeTab = _currentXPosition;
            switch (nextTabStop.Alignment)
            {
                case TabAlignment.Left:
                    _currentXPosition = ProbeAfterLeftAlignedTab(nextTabStop.Position.Point, out notFitting);
                    break;

                case TabAlignment.Right:
                    _currentXPosition = ProbeAfterRightAlignedTab(nextTabStop.Position.Point, out notFitting);
                    break;

                case TabAlignment.Center:
                    _currentXPosition = ProbeAfterCenterAlignedTab(nextTabStop.Position.Point, out notFitting);
                    break;

                case TabAlignment.Decimal:
                    _currentXPosition = ProbeAfterDecimalAlignedTab(nextTabStop.Position.Point, out notFitting);
                    break;
            }
            if (!notFitting)
            {
                // For correct right paragraph alignment with tabs
                if (!IgnoreHorizontalGrowth)
                    _currentLineWidth += _currentXPosition - xPositionBeforeTab;

                _tabOffsets.Add(new TabOffset(nextTabStop.Leader, _currentXPosition - xPositionBeforeTab));
                if (_currentLeaf != null)
                    _lastTab = _currentLeaf.Current;
            }

            return notFitting ? FormatResult.NewLine : FormatResult.Continue;
        }

        bool IsLineBreak(DocumentObject docObj)
        {
            if (docObj is Character)
            {
                if (((Character)docObj).SymbolName == SymbolName.LineBreak)
                    return true;
            }
            return false;
        }

        bool IsBlank(DocumentObject docObj)
        {
            if (docObj is Text)
            {
                if (((Text)docObj).Content == " ")
                    return true;
            }
            return false;
        }

        bool IsTab(DocumentObject docObj)
        {
            if (docObj is Character)
            {
                if (((Character)docObj).SymbolName == SymbolName.Tab)
                    return true;
            }
            return false;
        }

        bool IsSoftHyphen(DocumentObject docObj)
        {
            Text text = docObj as Text;
            if (text != null)
                return text.Content == "­";

            return false;
        }

        /// <summary>
        /// Probes the paragraph elements after a left aligned tab stop and returns the vertical text position to start at.
        /// </summary>
        /// <param name="tabStopPosition">Position of the tab to probe.</param>
        /// <param name="notFitting">Out parameter determining whether the tab causes a line break.</param>
        /// <returns>The new x-position to restart behind the tab.</returns>
        XUnit ProbeAfterLeftAlignedTab(XUnit tabStopPosition, out bool notFitting)
        {
            //--- Save ---------------------------------
            ParagraphIterator iter;
            int blankCount;
            XUnit xPosition;
            XUnit lineWidth;
            XUnit wordsWidth;
            XUnit blankWidth;
            SaveBeforeProbing(out iter, out blankCount, out wordsWidth, out xPosition, out lineWidth, out blankWidth);
            //------------------------------------------

            XUnit xPositionAfterTab = xPosition;
            _currentXPosition = _formattingArea.X + tabStopPosition.Point;

            notFitting = ProbeAfterTab();
            if (!notFitting)
                xPositionAfterTab = _formattingArea.X + tabStopPosition;

            //--- Restore ---------------------------------
            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
            //------------------------------------------
            return xPositionAfterTab;
        }

        /// <summary>
        /// Probes the paragraph elements after a right aligned tab stop and returns the vertical text position to start at.
        /// </summary>
        /// <param name="tabStopPosition">Position of the tab to probe.</param>
        /// <param name="notFitting">Out parameter determining whether the tab causes a line break.</param>
        /// <returns>The new x-position to restart behind the tab.</returns>
        XUnit ProbeAfterRightAlignedTab(XUnit tabStopPosition, out bool notFitting)
        {
            //--- Save ---------------------------------
            ParagraphIterator iter;
            int blankCount;
            XUnit xPosition;
            XUnit lineWidth;
            XUnit wordsWidth;
            XUnit blankWidth;
            SaveBeforeProbing(out iter, out blankCount, out wordsWidth, out xPosition, out lineWidth, out blankWidth);
            //------------------------------------------

            XUnit xPositionAfterTab = xPosition;

            notFitting = ProbeAfterTab();
            if (!notFitting && xPosition + _currentLineWidth <= _formattingArea.X + tabStopPosition)
                xPositionAfterTab = _formattingArea.X + tabStopPosition - _currentLineWidth;

            //--- Restore ------------------------------
            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
            //------------------------------------------
            return xPositionAfterTab;
        }

        Hyperlink GetHyperlink()
        {
            DocumentObject elements = DocumentRelations.GetParent(_currentLeaf.Current);
            DocumentObject parent = DocumentRelations.GetParent(elements);
            while (!(parent is Paragraph))
            {
                Hyperlink hyperlink = parent as Hyperlink;
                if (hyperlink != null)
                    return hyperlink;
                elements = DocumentRelations.GetParent(parent);
                parent = DocumentRelations.GetParent(elements);
            }
            return null;
        }

        /// <summary>
        /// Probes the paragraph elements after a right aligned tab stop and returns the vertical text position to start at.
        /// </summary>
        /// <param name="tabStopPosition">Position of the tab to probe.</param>
        /// <param name="notFitting">Out parameter determining whether the tab causes a line break.</param>
        /// <returns>The new x-position to restart behind the tab.</returns>
        XUnit ProbeAfterCenterAlignedTab(XUnit tabStopPosition, out bool notFitting)
        {
            //--- Save ---------------------------------
            ParagraphIterator iter;
            int blankCount;
            XUnit xPosition;
            XUnit lineWidth;
            XUnit wordsWidth;
            XUnit blankWidth;
            SaveBeforeProbing(out iter, out blankCount, out wordsWidth, out xPosition, out lineWidth, out blankWidth);
            //------------------------------------------

            XUnit xPositionAfterTab = xPosition;
            notFitting = ProbeAfterTab();

            if (!notFitting)
            {
                if (xPosition + _currentLineWidth / 2.0 <= _formattingArea.X + tabStopPosition)
                {
                    Rectangle rect = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);
                    if (_formattingArea.X + tabStopPosition + _currentLineWidth / 2.0 > rect.X + rect.Width - RightIndent)
                    {
                        //the text is too long on the right hand side of the tabstop => align to right indent.
                        xPositionAfterTab = rect.X +
                          rect.Width -
                          RightIndent -
                          _currentLineWidth;
                    }
                    else
                        xPositionAfterTab = _formattingArea.X + tabStopPosition - _currentLineWidth / 2;
                }
            }

            //--- Restore ------------------------------
            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
            //------------------------------------------
            return xPositionAfterTab;
        }

        /// <summary>
        /// Probes the paragraph elements after a right aligned tab stop and returns the vertical text position to start at.
        /// </summary>
        /// <param name="tabStopPosition">Position of the tab to probe.</param>
        /// <param name="notFitting">Out parameter determining whether the tab causes a line break.</param>
        /// <returns>The new x-position to restart behind the tab.</returns>
        XUnit ProbeAfterDecimalAlignedTab(XUnit tabStopPosition, out bool notFitting)
        {
            notFitting = false;
            ParagraphIterator savedLeaf = _currentLeaf;

            //Extra for auto tab after list symbol
            if (IsTab(_currentLeaf.Current))
                _currentLeaf = _currentLeaf.GetNextLeaf();
            if (_currentLeaf == null)
            {
                _currentLeaf = savedLeaf;
                return _currentXPosition + tabStopPosition;
            }
            VerticalLineInfo newVerticalInfo = CalcCurrentVerticalInfo();
            Rectangle fittingRect = _formattingArea.GetFittingRect(_currentYPosition, newVerticalInfo.Height);
            if (fittingRect == null)
            {
                notFitting = true;
                _currentLeaf = savedLeaf;
                return _currentXPosition;
            }

            if (IsPlainText(_currentLeaf.Current))
            {
                Text text = (Text)_currentLeaf.Current;
                string word = text.Content;
                int lastIndex = text.Content.LastIndexOfAny(new char[] { ',', '.' });
                if (lastIndex > 0)
                    word = word.Substring(0, lastIndex);

                XUnit wordLength = MeasureString(word);
                notFitting = _currentXPosition + wordLength >= _formattingArea.X + _formattingArea.Width + Tolerance;
                if (!notFitting)
                    return _formattingArea.X + tabStopPosition - wordLength;

                return _currentXPosition;
            }
            _currentLeaf = savedLeaf;
            return ProbeAfterRightAlignedTab(tabStopPosition, out notFitting);
        }

        void SaveBeforeProbing(out ParagraphIterator paragraphIter, out int blankCount, out XUnit wordsWidth, out XUnit xPosition, out XUnit lineWidth, out XUnit blankWidth)
        {
            paragraphIter = _currentLeaf;
            blankCount = _currentBlankCount;
            xPosition = _currentXPosition;
            lineWidth = _currentLineWidth;
            wordsWidth = _currentWordsWidth;
            blankWidth = _savedBlankWidth;
        }

        void RestoreAfterProbing(ParagraphIterator paragraphIter, int blankCount, XUnit wordsWidth, XUnit xPosition, XUnit lineWidth, XUnit blankWidth)
        {
            _currentLeaf = paragraphIter;
            _currentBlankCount = blankCount;
            _currentXPosition = xPosition;
            _currentLineWidth = lineWidth;
            _currentWordsWidth = wordsWidth;
            _savedBlankWidth = blankWidth;
        }

        /// <summary>
        /// Probes the paragraph after a tab.
        /// Caution: This Function resets the word count and line width before doing its work.
        /// </summary>
        /// <returns>True if the tab causes a linebreak.</returns>
        bool ProbeAfterTab()
        {
            _currentLineWidth = 0;
            _currentBlankCount = 0;
            //Extra for auto tab after list symbol

            //TODO: KLPO4KLPO: Check if this conditional statement is still required
            if (_currentLeaf != null && IsTab(_currentLeaf.Current))
                _currentLeaf = _currentLeaf.GetNextLeaf();

            bool wordAppeared = false;
            while (_currentLeaf != null && !IsLineBreak(_currentLeaf.Current) && !IsTab(_currentLeaf.Current))
            {
                FormatResult result = FormatElement(_currentLeaf.Current);
                if (result != FormatResult.Continue)
                    break;

                wordAppeared = wordAppeared || IsWordLikeElement(_currentLeaf.Current);
                _currentLeaf = _currentLeaf.GetNextLeaf();
            }
            return _currentLeaf != null && !IsLineBreak(_currentLeaf.Current) &&
              !IsTab(_currentLeaf.Current) && !wordAppeared;
        }

        /// <summary>
        /// Gets the next tab stop following the current x position.
        /// </summary>
        /// <returns>The searched tab stop.</returns>
        private TabStop GetNextTabStop()
        {
            ParagraphFormat format = _paragraph.Format;
            TabStops tabStops = format.TabStops;
            XUnit lastPosition = 0;

            foreach (TabStop tabStop in tabStops)
            {
                if (tabStop.Position.Point > _formattingArea.Width - RightIndent + Tolerance)
                    break;

                // Compare with "Tolerance" to prevent rounding errors from taking us one tabstop too far.
                if (tabStop.Position.Point + _formattingArea.X > _currentXPosition + Tolerance)
                    return tabStop;

                lastPosition = tabStop.Position.Point;
            }
            //Automatic tab stop: FirstLineIndent < 0 => automatic tab stop at LeftIndent.

            if (format.FirstLineIndent < 0 || 
                (format._listInfo != null && !format._listInfo.IsNull() && format.ListInfo.NumberPosition < format.LeftIndent))
            {
                XUnit leftIndent = format.LeftIndent.Point;
                if (_isFirstLine && _currentXPosition < leftIndent + _formattingArea.X)
                    return new TabStop(leftIndent.Point);
            }
            XUnit defaultTabStop = "1.25cm";
            if (!_paragraph.Document._defaultTabStop.IsNull)
                defaultTabStop = _paragraph.Document.DefaultTabStop.Point;

            XUnit currTabPos = defaultTabStop;
            while (currTabPos + _formattingArea.X <= _formattingArea.Width - RightIndent)
            {
                if (currTabPos > lastPosition && currTabPos + _formattingArea.X > _currentXPosition + Tolerance)
                    return new TabStop(currTabPos.Point);

                currTabPos += defaultTabStop;
            }
            return null;
        }

        /// <summary>
        /// Gets the horizontal position to start a new line.
        /// </summary>
        /// <returns>The position to start the line.</returns>
        XUnit StartXPosition
        {
            get
            {
                XUnit xPos = 0;

                if (_phase == Phase.Formatting)
                {
                    xPos = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height).X;
                    xPos += LeftIndent;
                }
                else //if (phase == Phase.Rendering)
                {
                    Area contentArea = _renderInfo.LayoutInfo.ContentArea;
                    //next lines for non fitting lines that produce an empty fitting rect:
                    XUnit rectX = contentArea.X;
                    XUnit rectWidth = contentArea.Width;

                    Rectangle fittingRect = contentArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);
                    if (fittingRect != null)
                    {
                        rectX = fittingRect.X;
                        rectWidth = fittingRect.Width;
                    }
                    switch (_paragraph.Format.Alignment)
                    {
                        case ParagraphAlignment.Left:
                        case ParagraphAlignment.Justify:
                            xPos = rectX;
                            xPos += LeftIndent;
                            break;

                        case ParagraphAlignment.Right:
                            xPos = rectX + rectWidth - RightIndent;
                            xPos -= _currentLineWidth;
                            break;

                        case ParagraphAlignment.Center:
                            xPos = rectX + (rectWidth + LeftIndent - RightIndent - _currentLineWidth) / 2.0;
                            break;
                    }
                }
                return xPos;
            }
        }

        /// <summary>
        /// Renders a single line.
        /// </summary>
        /// <param name="lineInfo"></param>
        void RenderLine(LineInfo lineInfo)
        {
            _currentVerticalInfo = lineInfo.Vertical;
            _currentLeaf = lineInfo.StartIter;
            _startLeaf = lineInfo.StartIter;
            _endLeaf = lineInfo.EndIter;
            _currentBlankCount = lineInfo.BlankCount;
            _currentLineWidth = lineInfo.LineWidth;
            _currentWordsWidth = lineInfo.WordsWidth;
            _currentXPosition = StartXPosition;
            _tabOffsets = lineInfo.TabOffsets;
            _lastTabPassed = lineInfo.LastTab == null;
            _lastTab = lineInfo.LastTab;

            _tabIdx = 0;

            bool ready = _currentLeaf == null;
            if (_isFirstLine)
                RenderListSymbol();

            while (!ready)
            {
                if (_currentLeaf.Current == lineInfo.EndIter.Current)
                    ready = true;

                if (_currentLeaf.Current == lineInfo.LastTab)
                    _lastTabPassed = true;
                RenderElement(_currentLeaf.Current);
                _currentLeaf = _currentLeaf.GetNextLeaf();
            }
            _currentYPosition += lineInfo.Vertical.Height;
            _isFirstLine = false;
        }

        void ReMeasureLine(ref LineInfo lineInfo)
        {
            //--- Save ---------------------------------
            ParagraphIterator iter;
            int blankCount;
            XUnit xPosition;
            XUnit lineWidth;
            XUnit wordsWidth;
            XUnit blankWidth;
            SaveBeforeProbing(out iter, out blankCount, out wordsWidth, out xPosition, out lineWidth, out blankWidth);
            bool origLastTabPassed = _lastTabPassed;
            //------------------------------------------
            _currentLeaf = lineInfo.StartIter;
            _endLeaf = lineInfo.EndIter;
            _formattingArea = _renderInfo.LayoutInfo.ContentArea;
            _tabOffsets = new List<TabOffset>();
            _currentLineWidth = 0;
            _currentWordsWidth = 0;

            Rectangle fittingRect = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);
            if (fittingRect != null)
            {
                _currentXPosition = fittingRect.X + LeftIndent;
                FormatListSymbol();
                bool goOn = true;
                while (goOn && _currentLeaf != null)
                {
                    if (_currentLeaf.Current == lineInfo.LastTab)
                        _lastTabPassed = true;

                    FormatElement(_currentLeaf.Current);

                    goOn = _currentLeaf != null && _currentLeaf.Current != _endLeaf.Current;
                    if (goOn)
                        _currentLeaf = _currentLeaf.GetNextLeaf();
                }
                lineInfo.LineWidth = _currentLineWidth;
                lineInfo.WordsWidth = _currentWordsWidth;
                lineInfo.BlankCount = _currentBlankCount;
                lineInfo.TabOffsets = _tabOffsets;
                lineInfo.ReMeasureLine = false;
                _lastTabPassed = origLastTabPassed;
            }
            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
        }

        XUnit CurrentWordDistance
        {
            get
            {
                if (_phase == Phase.Rendering &&
                  _paragraph.Format.Alignment == ParagraphAlignment.Justify && _lastTabPassed)
                {
                    if (_currentBlankCount >= 1 && !(_isLastLine && _renderInfo.FormatInfo.IsEnding))
                    {
                        Area contentArea = _renderInfo.LayoutInfo.ContentArea;
                        XUnit width = contentArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height).Width;
                        if (_lastTabPosition > 0)
                        {
                            width -= (_lastTabPosition -
                            contentArea.X);
                        }
                        else
                            width -= LeftIndent;

                        width -= RightIndent;
                        return (width - _currentWordsWidth) / (_currentBlankCount);
                    }
                }
                return MeasureString(" ");
            }
        }

        void RenderElement(DocumentObject docObj)
        {
            string typeName = docObj.GetType().Name;
            switch (typeName)
            {
                case "Text":
                    if (IsBlank(docObj))
                        RenderBlank();
                    else if (IsSoftHyphen(docObj))
                        RenderSoftHyphen();
                    else
                        RenderText((Text)docObj);
                    break;

                case "Character":
                    RenderCharacter((Character)docObj);
                    break;

                case "DateField":
                    RenderDateField((DateField)docObj);
                    break;

                case "InfoField":
                    RenderInfoField((InfoField)docObj);
                    break;

                case "NumPagesField":
                    RenderNumPagesField((NumPagesField)docObj);
                    break;

                case "PageField":
                    RenderPageField((PageField)docObj);
                    break;

                case "SectionField":
                    RenderSectionField((SectionField)docObj);
                    break;

                case "SectionPagesField":
                    RenderSectionPagesField((SectionPagesField)docObj);
                    break;

                case "BookmarkField":
                    RenderBookmarkField();
                    break;

                case "PageRefField":
                    RenderPageRefField((PageRefField)docObj);
                    break;

                case "Image":
                    RenderImage((Image)docObj);
                    break;
                //        default:
                //          throw new NotImplementedException(typeName + " is coming soon...");
            }
        }

        void RenderImage(Image image)
        {
            RenderInfo renderInfo = CurrentImageRenderInfo;
            XUnit top = CurrentBaselinePosition;
            Area contentArea = renderInfo.LayoutInfo.ContentArea;
            top -= contentArea.Height;
            RenderByInfos(_currentXPosition, top, new RenderInfo[] { renderInfo });

            RenderUnderline(contentArea.Width, true);
            RealizeHyperlink(contentArea.Width);

            _currentXPosition += contentArea.Width;
        }

        void RenderDateField(DateField dateField)
        {
            RenderWord(_fieldInfos.Date.ToString(dateField.Format));
        }

        void RenderInfoField(InfoField infoField)
        {
            RenderWord(GetDocumentInfo(infoField.Name));
        }

        void RenderNumPagesField(NumPagesField numPagesField)
        {
            RenderWord(GetFieldValue(numPagesField));
        }

        void RenderPageField(PageField pageField)
        {
            RenderWord(GetFieldValue(pageField));
        }

        void RenderSectionField(SectionField sectionField)
        {
            RenderWord(GetFieldValue(sectionField));
        }

        void RenderSectionPagesField(SectionPagesField sectionPagesField)
        {
            RenderWord(GetFieldValue(sectionPagesField));
        }

        void RenderBookmarkField()
        {
            RenderUnderline(0, false);
        }

        void RenderPageRefField(PageRefField pageRefField)
        {
            RenderWord(GetFieldValue(pageRefField));
        }

        void RenderCharacter(Character character)
        {
            switch (character.SymbolName)
            {
                case SymbolName.Blank:
                case SymbolName.Em:
                case SymbolName.Em4:
                case SymbolName.En:
                    RenderSpace(character);
                    break;
                case SymbolName.LineBreak:
                    RenderLinebreak();
                    break;

                case SymbolName.Tab:
                    RenderTab();
                    break;

                default:
                    RenderSymbol(character);
                    break;
            }
        }

        void RenderSpace(Character character)
        {
            XUnit width = GetSpaceWidth(character);
            RenderUnderline(width, false);
            RealizeHyperlink(width);
            _currentXPosition += width;
        }

        void RenderLinebreak()
        {
            RenderUnderline(0, false);
            RealizeHyperlink(0);
        }

        void RenderSymbol(Character character)
        {
            string sym = GetSymbol(character);
            string completeWord = sym;
            for (int idx = 1; idx < character.Count; ++idx)
                completeWord += sym;

            RenderWord(completeWord);
        }

        void RenderTab()
        {
            TabOffset tabOffset = NextTabOffset();
            RenderUnderline(tabOffset.Offset, false);
            RenderTabLeader(tabOffset);
            RealizeHyperlink(tabOffset.Offset);
            _currentXPosition += tabOffset.Offset;
            if (_currentLeaf.Current == _lastTab)
                _lastTabPosition = _currentXPosition;
        }

        void RenderTabLeader(TabOffset tabOffset)
        {
            string leaderString;
            switch (tabOffset.Leader)
            {
                case TabLeader.Dashes:
                    leaderString = "-";
                    break;

                case TabLeader.Dots:
                    leaderString = ".";
                    break;

                case TabLeader.Heavy:
                case TabLeader.Lines:
                    leaderString = "_";
                    break;

                case TabLeader.MiddleDot:
                    leaderString = "·";
                    break;

                default:
                    return;
            }
            XUnit leaderWidth = MeasureString(leaderString);
            XUnit xPosition = _currentXPosition;
            string drawString = "";

            while (xPosition + leaderWidth <= _currentXPosition + tabOffset.Offset)
            {
                drawString += leaderString;
                xPosition += leaderWidth;
            }
            Font font = CurrentDomFont;
            XFont xFont = CurrentFont;
            if (font.Subscript || font.Superscript)
                xFont = FontHandler.ToSubSuperFont(xFont);

            _gfx.DrawString(drawString, xFont, CurrentBrush, _currentXPosition, CurrentBaselinePosition);
        }

        TabOffset NextTabOffset()
        {
            TabOffset offset = _tabOffsets.Count > _tabIdx ? _tabOffsets[_tabIdx] :
              new TabOffset(0, 0);

            ++_tabIdx;
            return offset;
        }
        int _tabIdx;

        bool IgnoreBlank()
        {
            if (_currentLeaf == _startLeaf)
                return true;

            if (_endLeaf != null && _currentLeaf.Current == _endLeaf.Current)
                return true;

            ParagraphIterator nextIter = _currentLeaf.GetNextLeaf();
            while (nextIter != null && (IsBlank(nextIter.Current) || nextIter.Current is BookmarkField))
            {
                nextIter = nextIter.GetNextLeaf();
            }
            if (nextIter == null)
                return true;

            if (IsTab(nextIter.Current))
                return true;

            ParagraphIterator prevIter = _currentLeaf.GetPreviousLeaf();
            // Can be null if currentLeaf is the first leaf
            DocumentObject obj = prevIter != null ? prevIter.Current : null;
            while (obj != null && obj is BookmarkField)
            {
                prevIter = prevIter.GetPreviousLeaf();
                obj = prevIter != null ? prevIter.Current : null;
            }
            if (obj == null)
                return true;

            return IsBlank(obj) || IsTab(obj);
        }

        void RenderBlank()
        {
            if (!IgnoreBlank())
            {
                XUnit wordDistance = CurrentWordDistance;
                RenderUnderline(wordDistance, false);
                RealizeHyperlink(wordDistance);
                _currentXPosition += wordDistance;
            }
            else
            {
                RenderUnderline(0, false);
                RealizeHyperlink(0);
            }
        }

        void RenderSoftHyphen()
        {
            if (_currentLeaf.Current == _endLeaf.Current)
                RenderWord("-");
        }

        void RenderText(Text text)
        {
            RenderWord(text.Content);
        }

        void RenderWord(string word)
        {
            Font font = CurrentDomFont;
            XFont xFont = CurrentFont;
            if (font.Subscript || font.Superscript)
                xFont = FontHandler.ToSubSuperFont(xFont);

            _gfx.DrawString(word, xFont, CurrentBrush, _currentXPosition, CurrentBaselinePosition);
            XUnit wordWidth = MeasureString(word);
            RenderUnderline(wordWidth, true);
            RealizeHyperlink(wordWidth);
            _currentXPosition += wordWidth;
        }

        void StartHyperlink(XUnit left, XUnit top)
        {
            _hyperlinkRect = new XRect(left, top, 0, 0);
        }

        void EndHyperlink(Hyperlink hyperlink, XUnit right, XUnit bottom)
        {
            _hyperlinkRect.Width = right - _hyperlinkRect.X;
            _hyperlinkRect.Height = bottom - _hyperlinkRect.Y;
            PdfPage page = _gfx.PdfPage;
            if (page != null)
            {
                XRect rect = _gfx.Transformer.WorldToDefaultPage(_hyperlinkRect);

                switch (hyperlink.Type)
                {
                    case HyperlinkType.Local:
                        int pageRef = _fieldInfos.GetPhysicalPageNumber(hyperlink.Name);
                        if (pageRef > 0)
                            page.AddDocumentLink(new PdfRectangle(rect), pageRef);
                        break;

                    case HyperlinkType.Web:
                        page.AddWebLink(new PdfRectangle(rect), hyperlink.Name);
                        break;

                    case HyperlinkType.File:
                        page.AddFileLink(new PdfRectangle(rect), hyperlink.Name);
                        break;
                }
                _hyperlinkRect = new XRect();
            }
        }

        void RealizeHyperlink(XUnit width)
        {
            XUnit top = _currentYPosition;
            XUnit left = _currentXPosition;
            XUnit bottom = top + _currentVerticalInfo.Height;
            XUnit right = left + width;
            Hyperlink hyperlink = GetHyperlink();

            bool hyperlinkChanged = _currentHyperlink != hyperlink;

            if (hyperlinkChanged)
            {
                if (_currentHyperlink != null)
                    EndHyperlink(_currentHyperlink, left, bottom);

                if (hyperlink != null)
                    StartHyperlink(left, top);

                _currentHyperlink = hyperlink;
            }

            if (_currentLeaf.Current == _endLeaf.Current)
            {
                if (_currentHyperlink != null)
                    EndHyperlink(_currentHyperlink, right, bottom);

                _currentHyperlink = null;
            }
        }
        Hyperlink _currentHyperlink;
        XRect _hyperlinkRect;

        XUnit CurrentBaselinePosition
        {
            get
            {
                VerticalLineInfo verticalInfo = _currentVerticalInfo;
                XUnit position = _currentYPosition;

                Font font = CurrentDomFont;
                XFont xFont = CurrentFont;

                // $MaOs BUG: For LineSpacingRule.AtLeast the text should be positioned at the line bottom. Maybe verticalInfo.InherentlineSpace does not contain the lineSpacing value in this case.
                double setLineSpace = verticalInfo.InherentlineSpace;
                double standardFontLineSpace = xFont.GetHeight();

                // Set position to bottom of text.
                position += setLineSpace;
                if (font.Subscript)
                {
                    // Move sub-/superscaled descender up.
                    position -= FontHandler.GetSubSuperScaling(CurrentFont) * FontHandler.GetDescent(xFont);
                }
                else if (font.Superscript)
                {
                    // Set position to top of text.
                    position -= standardFontLineSpace;
                    // Move sub-/superscaled LineSpace down and descender up.
                    position += FontHandler.GetSubSuperScaling(CurrentFont) * (standardFontLineSpace - FontHandler.GetDescent(xFont));
                }
                else
                    // Move descender up.
                    position -= verticalInfo.Descent;

                return position;
            }
        }

        XBrush CurrentBrush
        {
            get
            {
                if (_currentLeaf != null)
                    return FontHandler.FontColorToXBrush(CurrentDomFont);

                return null;
            }
        }

        private void InitRendering()
        {
            _phase = Phase.Rendering;

            ParagraphFormatInfo parFormatInfo = (ParagraphFormatInfo)_renderInfo.FormatInfo;
            if (parFormatInfo.LineCount == 0)
                return;
            _isFirstLine = parFormatInfo.IsStarting;

            LineInfo lineInfo = parFormatInfo.GetFirstLineInfo();
            Area contentArea = _renderInfo.LayoutInfo.ContentArea;
            _currentYPosition = contentArea.Y + TopBorderOffset;
            // StL: GetFittingRect liefert manchmal null
            Rectangle rect = contentArea.GetFittingRect(_currentYPosition, lineInfo.Vertical.Height);
            if (rect != null)
                _currentXPosition = rect.X;
            _currentLineWidth = 0;
        }

        /// <summary>
        /// Initializes this instance for formatting.
        /// </summary>
        /// <param name="area">The area for formatting</param>
        /// <param name="previousFormatInfo">A previous format info.</param>
        /// <returns>False, if nothing of the paragraph will fit the area any more.</returns>
        private bool InitFormat(Area area, FormatInfo previousFormatInfo)
        {
            _phase = Phase.Formatting;

            _tabOffsets = new List<TabOffset>();

            ParagraphFormatInfo prevParaFormatInfo = (ParagraphFormatInfo)previousFormatInfo;
            if (previousFormatInfo == null || prevParaFormatInfo.LineCount == 0)
            {
                ((ParagraphFormatInfo)_renderInfo.FormatInfo)._isStarting = true;
                ParagraphIterator parIt = new ParagraphIterator(_paragraph.Elements);
                _currentLeaf = parIt.GetFirstLeaf();
                _isFirstLine = true;
            }
            else
            {
                _currentLeaf = prevParaFormatInfo.GetLastLineInfo().EndIter.GetNextLeaf();
                _isFirstLine = false;
                ((ParagraphFormatInfo)_renderInfo.FormatInfo)._isStarting = false;
            }

            _startLeaf = _currentLeaf;
            _currentVerticalInfo = CalcCurrentVerticalInfo();
            _currentYPosition = area.Y + TopBorderOffset;
            _formattingArea = area;
            Rectangle rect = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);
            if (rect == null)
                return false;

            _currentXPosition = rect.X + LeftIndent;
            if (_isFirstLine)
                FormatListSymbol();

            return true;
        }

        /// <summary>
        /// Gets information necessary to render or measure the list symbol.
        /// </summary>
        /// <param name="symbol">The text to list symbol to render or measure</param>
        /// <param name="font">The font to use for rendering or measuring.</param>
        /// <returns>True, if a symbol needs to be rendered.</returns>
        bool GetListSymbol(out string symbol, out XFont font)
        {
            font = null;
            symbol = null;
            ParagraphFormatInfo formatInfo = (ParagraphFormatInfo)_renderInfo.FormatInfo;
            if (_phase == Phase.Formatting)
            {
                ParagraphFormat format = _paragraph.Format;
                if (format._listInfo != null && !format._listInfo.IsNull())
                {
                    ListInfo listInfo = format.ListInfo;
                    double size = format.Font.Size;
                    XFontStyle style = FontHandler.GetXStyle(format.Font);

                    switch (listInfo.ListType)
                    {
                        case ListType.BulletList1:
                            symbol = "·";
                            font = new XFont("Symbol", size, style);
                            break;

                        case ListType.BulletList2:
                            symbol = "o";
                            font = new XFont("Courier New", size, style);
                            break;

                        case ListType.BulletList3:
                            symbol = "§";
                            font = new XFont("Wingdings", size, style);
                            break;

                        case ListType.NumberList1:
                            symbol = _documentRenderer.NextListNumber(listInfo) + ".";
                            font = FontHandler.FontToXFont(format.Font, _gfx.MUH);
                            break;

                        case ListType.NumberList2:
                            symbol = _documentRenderer.NextListNumber(listInfo) + ")";
                            font = FontHandler.FontToXFont(format.Font, _gfx.MUH);
                            break;

                        case ListType.NumberList3:
                            symbol = NumberFormatter.Format(_documentRenderer.NextListNumber(listInfo), "alphabetic") + ")";
                            font = FontHandler.FontToXFont(format.Font, _gfx.MUH);
                            break;
                    }
                    formatInfo.ListFont = font;
                    formatInfo.ListSymbol = symbol;
                    return true;
                }
            }
            else
            {
                if (formatInfo.ListFont != null && formatInfo.ListSymbol != null)
                {
                    font = formatInfo.ListFont;
                    symbol = formatInfo.ListSymbol;
                    return true;
                }
            }
            return false;
        }

        XUnit LeftIndent
        {
            get
            {
                ParagraphFormat format = _paragraph.Format;
                XUnit leftIndent = format.LeftIndent.Point;
                if (_isFirstLine)
                {
                    if (format._listInfo != null && !format._listInfo.IsNull())
                    {
                        if (!format.ListInfo._numberPosition.IsNull)
                            return format.ListInfo.NumberPosition.Point;
                        if (format._firstLineIndent.IsNull)
                            return 0;
                    }
                    return leftIndent + _paragraph.Format.FirstLineIndent.Point;
                }
                return leftIndent;
            }
        }

        XUnit RightIndent
        {
            get
            {
                return _paragraph.Format.RightIndent.Point;
            }
        }

        /// <summary>
        /// Formats the paragraph by performing line breaks etc.
        /// </summary>
        /// <param name="area">The area in which to render.</param>
        /// <param name="previousFormatInfo">The format info that was obtained on formatting the same paragraph on a previous area.</param>
        public override void Format(Area area, FormatInfo previousFormatInfo)
        {
            ParagraphFormatInfo formatInfo = ((ParagraphFormatInfo)_renderInfo.FormatInfo);
            if (!InitFormat(area, previousFormatInfo))
            {
                formatInfo._isStarting = false;
                return;
            }
            formatInfo._isEnding = true;

            FormatResult lastResult = FormatResult.Continue;
            while (_currentLeaf != null)
            {
                FormatResult result = FormatElement(_currentLeaf.Current);
                switch (result)
                {
                    case FormatResult.Ignore:
                        _currentLeaf = _currentLeaf.GetNextLeaf();
                        break;

                    case FormatResult.Continue:
                        lastResult = result;
                        _currentLeaf = _currentLeaf.GetNextLeaf();
                        break;

                    case FormatResult.NewLine:
                        lastResult = result;
                        StoreLineInformation();
                        if (!StartNewLine())
                        {
                            result = FormatResult.NewArea;
                            formatInfo._isEnding = false;
                        }
                        break;
                }
                if (result == FormatResult.NewArea)
                {
                    lastResult = result;
                    formatInfo._isEnding = false;
                    break;
                }
            }
            if (formatInfo.IsEnding && lastResult != FormatResult.NewLine)
                StoreLineInformation();

            formatInfo.ImageRenderInfos = _imageRenderInfos;
            FinishLayoutInfo();
        }

        /// <summary>
        /// Finishes the layout info by calculating starting and trailing heights.
        /// </summary>
        private void FinishLayoutInfo()
        {
            LayoutInfo layoutInfo = _renderInfo.LayoutInfo;
            ParagraphFormat format = _paragraph.Format;
            ParagraphFormatInfo parInfo = (ParagraphFormatInfo)_renderInfo.FormatInfo;
            layoutInfo.MinWidth = _minWidth;
            layoutInfo.KeepTogether = format.KeepTogether;

            if (parInfo.IsComplete)
            {
                int limitOfLines = 1;
                if (parInfo._widowControl)
                    limitOfLines = 3;

                if (parInfo.LineCount <= limitOfLines)
                    layoutInfo.KeepTogether = true;
            }
            if (parInfo.IsStarting)
            {
                layoutInfo.MarginTop = format.SpaceBefore.Point;
                layoutInfo.PageBreakBefore = format.PageBreakBefore;
            }
            else
            {
                layoutInfo.MarginTop = 0;
                layoutInfo.PageBreakBefore = false;
            }

            if (parInfo.IsEnding)
            {
                layoutInfo.MarginBottom = _paragraph.Format.SpaceAfter.Point;
                layoutInfo.KeepWithNext = _paragraph.Format.KeepWithNext;
            }
            else
            {
                layoutInfo.MarginBottom = 0;
                layoutInfo.KeepWithNext = false;
            }
            if (parInfo.LineCount > 0)
            {
                XUnit startingHeight = parInfo.GetFirstLineInfo().Vertical.Height;
                if (parInfo._isStarting && _paragraph.Format.WidowControl && parInfo.LineCount >= 2)
                    startingHeight += parInfo.GetLineInfo(1).Vertical.Height;

                layoutInfo.StartingHeight = startingHeight;

                XUnit trailingHeight = parInfo.GetLastLineInfo().Vertical.Height;

                if (parInfo.IsEnding && _paragraph.Format.WidowControl && parInfo.LineCount >= 2)
                    trailingHeight += parInfo.GetLineInfo(parInfo.LineCount - 2).Vertical.Height;

                layoutInfo.TrailingHeight = trailingHeight;
            }
        }


        private XUnit PopSavedBlankWidth()
        {
            XUnit width = _savedBlankWidth;
            _savedBlankWidth = 0;
            return width;
        }

        private void SaveBlankWidth(XUnit blankWidth)
        {
            _savedBlankWidth = blankWidth;
        }
        XUnit _savedBlankWidth = 0;

        /// <summary>
        /// Processes the elements when formatting.
        /// </summary>
        /// <param name="docObj"></param>
        /// <returns></returns>
        FormatResult FormatElement(DocumentObject docObj)
        {
            switch (docObj.GetType().Name)
            {
                case "Text":
                    if (IsBlank(docObj))
                        return FormatBlank();
                    if (IsSoftHyphen(docObj))
                        return FormatSoftHyphen();
                    return FormatText((Text)docObj);

                case "Character":
                    return FormatCharacter((Character)docObj);

                case "DateField":
                    return FormatDateField((DateField)docObj);

                case "InfoField":
                    return FormatInfoField((InfoField)docObj);

                case "NumPagesField":
                    return FormatNumPagesField((NumPagesField)docObj);

                case "PageField":
                    return FormatPageField((PageField)docObj);

                case "SectionField":
                    return FormatSectionField((SectionField)docObj);

                case "SectionPagesField":
                    return FormatSectionPagesField((SectionPagesField)docObj);

                case "BookmarkField":
                    return FormatBookmarkField((BookmarkField)docObj);

                case "PageRefField":
                    return FormatPageRefField((PageRefField)docObj);

                case "Image":
                    return FormatImage((Image)docObj);

                default:
                    return FormatResult.Continue;
            }
        }

        FormatResult FormatImage(Image image)
        {
            XUnit width = CurrentImageRenderInfo.LayoutInfo.ContentArea.Width;
            return FormatAsWord(width);
        }

        RenderInfo CalcImageRenderInfo(Image image)
        {
            Renderer renderer = Create(_gfx, _documentRenderer, image, _fieldInfos);
            renderer.Format(new Rectangle(0, 0, double.MaxValue, double.MaxValue), null);

            return renderer.RenderInfo;
        }

        bool IsPlainText(DocumentObject docObj)
        {
            if (docObj is Text)
                return !IsSoftHyphen(docObj) && !IsBlank(docObj);

            return false;
        }

        bool IsSymbol(DocumentObject docObj)
        {
            if (docObj is Character)
            {
                return !IsSpaceCharacter(docObj) && !IsTab(docObj) && !IsLineBreak(docObj);
            }
            return false;
        }

        bool IsSpaceCharacter(DocumentObject docObj)
        {
            Character character = docObj as Character;
            if (character != null)
            {
                switch ((character).SymbolName)
                {
                    case SymbolName.Blank:
                    case SymbolName.Em:
                    case SymbolName.Em4:
                    case SymbolName.En:
                        return true;
                }
            }
            return false;
        }

        bool IsWordLikeElement(DocumentObject docObj)
        {
            if (IsPlainText(docObj))
                return true;

            if (IsRenderedField(docObj))
                return true;

            if (IsSymbol(docObj))
                return true;


            return false;
        }

        FormatResult FormatBookmarkField(BookmarkField bookmarkField)
        {
            _fieldInfos.AddBookmark(bookmarkField.Name);
            return FormatResult.Ignore;
        }

        FormatResult FormatPageRefField(PageRefField pageRefField)
        {
            _reMeasureLine = true;
            string fieldValue = GetFieldValue(pageRefField);
            return FormatWord(fieldValue);
        }

        FormatResult FormatNumPagesField(NumPagesField numPagesField)
        {
            _reMeasureLine = true;
            string fieldValue = GetFieldValue(numPagesField);
            return FormatWord(fieldValue);
        }

        FormatResult FormatPageField(PageField pageField)
        {
            _reMeasureLine = true;
            string fieldValue = GetFieldValue(pageField);
            return FormatWord(fieldValue);
        }

        FormatResult FormatSectionField(SectionField sectionField)
        {
            _reMeasureLine = true;
            string fieldValue = GetFieldValue(sectionField);
            return FormatWord(fieldValue);
        }

        FormatResult FormatSectionPagesField(SectionPagesField sectionPagesField)
        {
            _reMeasureLine = true;
            string fieldValue = GetFieldValue(sectionPagesField);
            return FormatWord(fieldValue);
        }

        /// <summary>
        /// Helper function for formatting word-like elements like text and fields.
        /// </summary>
        FormatResult FormatWord(string word)
        {
            XUnit width = MeasureString(word);
            return FormatAsWord(width);
        }

        XUnit _savedWordWidth = 0;

        /// <summary>
        /// When rendering a justified paragraph, only the part after the last tab stop needs remeasuring.
        /// </summary>
        private bool IgnoreHorizontalGrowth
        {
            get
            {
                return _phase == Phase.Rendering && _paragraph.Format.Alignment == ParagraphAlignment.Justify &&
                    !_lastTabPassed;
            }
        }

        FormatResult FormatAsWord(XUnit width)
        {
            VerticalLineInfo newVertInfo = CalcCurrentVerticalInfo();

            Rectangle rect = _formattingArea.GetFittingRect(_currentYPosition, newVertInfo.Height + BottomBorderOffset);
            if (rect == null)
                return FormatResult.NewArea;

            if (_currentXPosition + width <= rect.X + rect.Width - RightIndent + Tolerance)
            {
                _savedWordWidth = width;
                _currentXPosition += width;
                // For Tabs in justified context
                if (!IgnoreHorizontalGrowth)
                    _currentWordsWidth += width;
                if (_savedBlankWidth > 0)
                {
                    // For Tabs in justified context
                    if (!IgnoreHorizontalGrowth)
                        ++_currentBlankCount;
                }
                // For Tabs in justified context
                if (!IgnoreHorizontalGrowth)
                    _currentLineWidth += width + PopSavedBlankWidth();
                _currentVerticalInfo = newVertInfo;
                _minWidth = Math.Max(_minWidth, width);
                return FormatResult.Continue;
            }
            else
            {
                _savedWordWidth = width;
                return FormatResult.NewLine;
            }
        }

        FormatResult FormatDateField(DateField dateField)
        {
            _reMeasureLine = true;
            string estimatedFieldValue = DateTime.Now.ToString(dateField.Format);
            return FormatWord(estimatedFieldValue);
        }

        FormatResult FormatInfoField(InfoField infoField)
        {
            string fieldValue = GetDocumentInfo(infoField.Name);
            if (fieldValue != "")
                return FormatWord(fieldValue);

            return FormatResult.Continue;
        }

        string GetDocumentInfo(string name)
        {
            string valueName;
            switch (name.ToLower())
            {
                case "title":
                    valueName = "Title";
                    break;

                case "author":
                    valueName = "Author";
                    break;

                case "keywords":
                    valueName = "Keywords";
                    break;

                case "subject":
                    valueName = "Subject";
                    break;

                default:
                    return String.Empty;
            }
            return _paragraph.Document.Info.GetValue(valueName).ToString();

            //string docInfoValue = "";
            //string[] enumNames = Enum.GetNames(typeof(InfoFieldType));
            //foreach (string enumName in enumNames)
            //{
            //  if (String.Compare(name, enumName, true) == 0)
            //  {
            //    docInfoValue = paragraph.Document.Info.GetValue(enumName).ToString();
            //    break;
            //  }
            //}
            //return docInfoValue;
        }

        Area GetShadingArea()
        {
            Area contentArea = _renderInfo.LayoutInfo.ContentArea;
            ParagraphFormat format = _paragraph.Format;
            XUnit left = contentArea.X;
            left += format.LeftIndent;
            if (format.FirstLineIndent < 0)
                left += format.FirstLineIndent;

            XUnit top = contentArea.Y;
            XUnit bottom = contentArea.Y + contentArea.Height;
            XUnit right = contentArea.X + contentArea.Width;
            right -= format.RightIndent;

            if (_paragraph.Format._borders != null && !_paragraph.Format._borders.IsNull())
            {
                Borders borders = format.Borders;
                BordersRenderer bordersRenderer = new BordersRenderer(borders, _gfx);

                if (_renderInfo.FormatInfo.IsStarting)
                    top += bordersRenderer.GetWidth(BorderType.Top);
                if (_renderInfo.FormatInfo.IsEnding)
                    bottom -= bordersRenderer.GetWidth(BorderType.Bottom);

                left -= borders.DistanceFromLeft;
                right += borders.DistanceFromRight;
            }
            return new Rectangle(left, top, right - left, bottom - top);
        }

        void RenderShading()
        {
            if (_paragraph.Format._shading == null || _paragraph.Format._shading.IsNull())
                return;

            ShadingRenderer shadingRenderer = new ShadingRenderer(_gfx, _paragraph.Format.Shading);
            Area area = GetShadingArea();

            shadingRenderer.Render(area.X, area.Y, area.Width, area.Height);
        }


        void RenderBorders()
        {
            if (_paragraph.Format._borders == null || _paragraph.Format._borders.IsNull())
                return;

            Area shadingArea = GetShadingArea();
            XUnit left = shadingArea.X;
            XUnit top = shadingArea.Y;
            XUnit bottom = shadingArea.Y + shadingArea.Height;
            XUnit right = shadingArea.X + shadingArea.Width;

            Borders borders = _paragraph.Format.Borders;
            BordersRenderer bordersRenderer = new BordersRenderer(borders, _gfx);
            XUnit borderWidth = bordersRenderer.GetWidth(BorderType.Left);
            if (borderWidth > 0)
            {
                left -= borderWidth;
                bordersRenderer.RenderVertically(BorderType.Left, left, top, bottom - top);
            }

            borderWidth = bordersRenderer.GetWidth(BorderType.Right);
            if (borderWidth > 0)
            {
                bordersRenderer.RenderVertically(BorderType.Right, right, top, bottom - top);
                right += borderWidth;
            }

            borderWidth = bordersRenderer.GetWidth(BorderType.Top);
            if (_renderInfo.FormatInfo.IsStarting && borderWidth > 0)
            {
                top -= borderWidth;
                bordersRenderer.RenderHorizontally(BorderType.Top, left, top, right - left);
            }

            borderWidth = bordersRenderer.GetWidth(BorderType.Bottom);
            if (_renderInfo.FormatInfo.IsEnding && borderWidth > 0)
            {
                bordersRenderer.RenderHorizontally(BorderType.Bottom, left, bottom, right - left);
            }
        }

        XUnit MeasureString(string word)
        {
            XFont xFont = CurrentFont;
            XUnit width = _gfx.MeasureString(word, xFont, StringFormat).Width;
            Font font = CurrentDomFont;

            if (font.Subscript || font.Superscript)
                width *= FontHandler.GetSubSuperScaling(xFont);

            return width;
        }

        XUnit GetSpaceWidth(Character character)
        {
            XUnit width = 0;
            switch (character.SymbolName)
            {
                case SymbolName.Blank:
                    width = MeasureString(" ");
                    break;
                case SymbolName.Em:
                    width = MeasureString("m");
                    break;
                case SymbolName.Em4:
                    width = 0.25 * MeasureString("m");
                    break;
                case SymbolName.En:
                    width = MeasureString("n");
                    break;
            }
            return width * character.Count;
        }

        void RenderListSymbol()
        {
            string symbol;
            XFont font;
            if (GetListSymbol(out symbol, out font))
            {
                XBrush brush = FontHandler.FontColorToXBrush(_paragraph.Format.Font);
                _gfx.DrawString(symbol, font, brush, _currentXPosition, CurrentBaselinePosition);
                _currentXPosition += _gfx.MeasureString(symbol, font, StringFormat).Width;
                TabOffset tabOffset = NextTabOffset();
                _currentXPosition += tabOffset.Offset;
                _lastTabPosition = _currentXPosition;
            }
        }

        void FormatListSymbol()
        {
            string symbol;
            XFont font;
            if (GetListSymbol(out symbol, out font))
            {
                _currentVerticalInfo = CalcVerticalInfo(font);
                _currentXPosition += _gfx.MeasureString(symbol, font, StringFormat).Width;
                FormatTab();
            }
        }

        FormatResult FormatSpace(Character character)
        {
            XUnit width = GetSpaceWidth(character);
            return FormatAsWord(width);
        }

        static string GetSymbol(Character character)
        {
            char ch;
            switch (character.SymbolName)
            {
                case SymbolName.Euro:
                    ch = '€';
                    break;

                case SymbolName.Copyright:
                    ch = '©';
                    break;

                case SymbolName.Trademark:
                    ch = '™';
                    break;

                case SymbolName.RegisteredTrademark:
                    ch = '®';
                    break;

                case SymbolName.Bullet:
                    ch = '•';
                    break;

                case SymbolName.Not:
                    ch = '¬';
                    break;
                //REM: Non-breakable blanks are still ignored.
                //        case SymbolName.SymbolNonBreakableBlank:
                //          return "\xA0";
                //          break;

                case SymbolName.EmDash:
                    ch = '—';
                    break;

                case SymbolName.EnDash:
                    ch = '–';
                    break;

                default:
                    char c = character.Char;
                    char[] chars = Encoding.UTF8.GetChars(new byte[] { (byte)c });
                    //#if !SILVERLIGHT
                    //                    char[] chars = System.Text.Encoding.Default.GetChars(new byte[] { (byte)c });
                    //#else
                    //                    char[] chars = System.Text.Encoding.UTF8.GetChars(new byte[] { (byte)c });
                    //#endif
                    ch = chars[0];
                    break;
            }
            string returnString = "";
            returnString += ch;
            int count = character.Count;
            while (--count > 0)
                returnString += ch;
            return returnString;
        }

        FormatResult FormatSymbol(Character character)
        {
            return FormatWord(GetSymbol(character));
        }

        /// <summary>
        /// Processes (measures) a special character within text.
        /// </summary>
        /// <param name="character">The character to process.</param>
        /// <returns>True if the character should start at a new line.</returns>
        FormatResult FormatCharacter(Character character)
        {
            switch (character.SymbolName)
            {
                case SymbolName.Blank:
                case SymbolName.Em:
                case SymbolName.Em4:
                case SymbolName.En:
                    return FormatSpace(character);

                case SymbolName.LineBreak:
                    return FormatLineBreak();

                case SymbolName.Tab:
                    return FormatTab();

                default:
                    return FormatSymbol(character);
            }
        }

        /// <summary>
        /// Processes (measures) a blank.
        /// </summary>
        /// <returns>True if the blank causes a line break.</returns>
        FormatResult FormatBlank()
        {
            if (IgnoreBlank())
                return FormatResult.Ignore;

            _savedWordWidth = 0;
            XUnit width = MeasureString(" ");
            VerticalLineInfo newVertInfo = CalcCurrentVerticalInfo();
            Rectangle rect = _formattingArea.GetFittingRect(_currentYPosition, newVertInfo.Height + BottomBorderOffset);
            if (rect == null)
                return FormatResult.NewArea;

            if (width + _currentXPosition <= rect.X + rect.Width + Tolerance)
            {
                _currentXPosition += width;
                _currentVerticalInfo = newVertInfo;
                SaveBlankWidth(width);
                return FormatResult.Continue;
            }
            return FormatResult.NewLine;
        }

        FormatResult FormatLineBreak()
        {
            if (_phase != Phase.Rendering)
                _currentLeaf = _currentLeaf.GetNextLeaf();

            _savedWordWidth = 0;
            return FormatResult.NewLine;
        }

        /// <summary>
        /// Processes a text element during formatting.
        /// </summary>
        /// <param name="text">The text element to measure.</param>
        FormatResult FormatText(Text text)
        {
            return FormatWord(text.Content);
        }

        FormatResult FormatSoftHyphen()
        {
            if (_currentLeaf.Current == _startLeaf.Current)
                return FormatResult.Continue;

            ParagraphIterator nextIter = _currentLeaf.GetNextLeaf();
            ParagraphIterator prevIter = _currentLeaf.GetPreviousLeaf();
            // nextIter can be null if the soft hyphen is at the end of a paragraph. To prevent a crash, we jump out if nextIter is null.
            if (!IsWordLikeElement(prevIter.Current) || nextIter == null || !IsWordLikeElement(nextIter.Current))
                return FormatResult.Continue;

            //--- Save ---------------------------------
            ParagraphIterator iter;
            int blankCount;
            XUnit xPosition;
            XUnit lineWidth;
            XUnit wordsWidth;
            XUnit blankWidth;
            SaveBeforeProbing(out iter, out blankCount, out wordsWidth, out xPosition, out lineWidth, out blankWidth);
            //------------------------------------------
            _currentLeaf = nextIter;
            FormatResult result = FormatElement(nextIter.Current);

            //--- Restore ------------------------------
            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
            //------------------------------------------
            if (result == FormatResult.Continue)
                return FormatResult.Continue;

            RestoreAfterProbing(iter, blankCount, wordsWidth, xPosition, lineWidth, blankWidth);
            Rectangle fittingRect = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);

            XUnit hyphenWidth = MeasureString("-");
            if (xPosition + hyphenWidth <= fittingRect.X + fittingRect.Width + Tolerance
                // If one word fits, but not the hyphen, the formatting must continue with the next leaf
                || prevIter.Current == _startLeaf.Current)
            {
                // For Tabs in justified context
                if (!IgnoreHorizontalGrowth)
                {
                    _currentWordsWidth += hyphenWidth;
                    _currentLineWidth += hyphenWidth;
                }
                _currentLeaf = nextIter;
                return FormatResult.NewLine;
            }
            else
            {
                _currentWordsWidth -= _savedWordWidth;
                _currentLineWidth -= _savedWordWidth;
                _currentLineWidth -= GetPreviousBlankWidth(prevIter);
                _currentLeaf = prevIter;
                return FormatResult.NewLine;
            }
        }

        XUnit GetPreviousBlankWidth(ParagraphIterator beforeIter)
        {
            XUnit width = 0;
            ParagraphIterator savedIter = _currentLeaf;
            _currentLeaf = beforeIter.GetPreviousLeaf();
            while (_currentLeaf != null)
            {
                if (_currentLeaf.Current is BookmarkField)
                    _currentLeaf = _currentLeaf.GetPreviousLeaf();
                else if (IsBlank(_currentLeaf.Current))
                {
                    if (!IgnoreBlank())
                        width = CurrentWordDistance;

                    break;
                }
                else
                    break;
            }
            _currentLeaf = savedIter;
            return width;
        }

        void HandleNonFittingLine()
        {
            if (_currentLeaf != null)
            {
                if (_savedWordWidth > 0)
                {
                    _currentWordsWidth = _savedWordWidth;
                    _currentLineWidth = _savedWordWidth;
                }
                _currentLeaf = _currentLeaf.GetNextLeaf();
                _currentYPosition += _currentVerticalInfo.Height;
                _currentVerticalInfo = new VerticalLineInfo();
            }
        }

        /// <summary>
        /// Starts a new line by resetting measuring values.
        /// Do not call before the first first line is formatted!
        /// </summary>
        /// <returns>True, if the new line may fit the formatting area.</returns>
        bool StartNewLine()
        {
            _tabOffsets = new List<TabOffset>();
            _lastTab = null;
            _lastTabPosition = 0;
            _currentYPosition += _currentVerticalInfo.Height;

            Rectangle rect = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height + BottomBorderOffset);
            if (rect == null)
                return false;

            _isFirstLine = false;
            _currentXPosition = StartXPosition; // depends on "currentVerticalInfo"
            _currentVerticalInfo = new VerticalLineInfo();
            _currentVerticalInfo = CalcCurrentVerticalInfo();
            _startLeaf = _currentLeaf;
            _currentBlankCount = 0;
            _currentWordsWidth = 0;
            _currentLineWidth = 0;
            return true;
        }
        /// <summary>
        /// Stores all line information.
        /// </summary>
        void StoreLineInformation()
        {
            PopSavedBlankWidth();

            XUnit topBorderOffset = TopBorderOffset;
            Area contentArea = _renderInfo.LayoutInfo.ContentArea;
            if (topBorderOffset > 0)//May only occure for the first line.
                contentArea = _formattingArea.GetFittingRect(_formattingArea.Y, topBorderOffset);

            if (contentArea == null)
                contentArea = _formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height);
            else
                contentArea = contentArea.Unite(_formattingArea.GetFittingRect(_currentYPosition, _currentVerticalInfo.Height));

            XUnit bottomBorderOffset = BottomBorderOffset;
            if (bottomBorderOffset > 0)
                contentArea = contentArea.Unite(_formattingArea.GetFittingRect(_currentYPosition + _currentVerticalInfo.Height, bottomBorderOffset));

            LineInfo lineInfo = new LineInfo();
            lineInfo.Vertical = _currentVerticalInfo;

            if (_startLeaf != null && _startLeaf == _currentLeaf)
                HandleNonFittingLine();

            lineInfo.LastTab = _lastTab;
            _renderInfo.LayoutInfo.ContentArea = contentArea;

            lineInfo.StartIter = _startLeaf;

            if (_currentLeaf == null)
                lineInfo.EndIter = new ParagraphIterator(_paragraph.Elements).GetLastLeaf();
            else
                lineInfo.EndIter = _currentLeaf.GetPreviousLeaf();

            lineInfo.BlankCount = _currentBlankCount;

            lineInfo.WordsWidth = _currentWordsWidth;

            lineInfo.LineWidth = _currentLineWidth;
            lineInfo.TabOffsets = _tabOffsets;
            lineInfo.ReMeasureLine = _reMeasureLine;

            _savedWordWidth = 0;
            _reMeasureLine = false;
            ((ParagraphFormatInfo)_renderInfo.FormatInfo).AddLineInfo(lineInfo);
        }

        /// <summary>
        /// Gets the top border offset for the first line, else 0.
        /// </summary>
        XUnit TopBorderOffset
        {
            get
            {
                XUnit offset = 0;
                if (_isFirstLine && _paragraph.Format._borders != null && !_paragraph.Format._borders.IsNull())
                {
                    offset += _paragraph.Format.Borders.DistanceFromTop;
                    if (_paragraph.Format._borders != null && !_paragraph.Format._borders.IsNull())
                    {
                        BordersRenderer bordersRenderer = new BordersRenderer(_paragraph.Format.Borders, _gfx);
                        offset += bordersRenderer.GetWidth(BorderType.Top);
                    }
                }
                return offset;
            }
        }

        bool IsLastVisibleLeaf
        {
            get
            {
                // REM: Code is missing here for blanks, bookmarks etc. which might be invisible.
                if (_currentLeaf.IsLastLeaf)
                    return true;

                return false;
            }
        }
        /// <summary>
        /// Gets the bottom border offset for the last line, else 0.
        /// </summary>
        XUnit BottomBorderOffset
        {
            get
            {
                XUnit offset = 0;
                //while formatting, it is impossible to determine whether we are in the last line until the last visible leaf is reached.
                if ((_phase == Phase.Formatting && (_currentLeaf == null || IsLastVisibleLeaf))
                  || (_phase == Phase.Rendering && (_isLastLine)))
                {
                    if (_paragraph.Format._borders != null && !_paragraph.Format._borders.IsNull())
                    {
                        offset += _paragraph.Format.Borders.DistanceFromBottom;
                        BordersRenderer bordersRenderer = new BordersRenderer(_paragraph.Format.Borders, _gfx);
                        offset += bordersRenderer.GetWidth(BorderType.Bottom);
                    }
                }
                return offset;
            }
        }

        VerticalLineInfo CalcCurrentVerticalInfo()
        {
            return CalcVerticalInfo(CurrentFont);
        }

        VerticalLineInfo CalcVerticalInfo(XFont font)
        {
            ParagraphFormat paragraphFormat = _paragraph.Format;
            LineSpacingRule spacingRule = paragraphFormat.LineSpacingRule;
            XUnit lineHeight = 0;

            XUnit descent = FontHandler.GetDescent(font);
            descent = Math.Max(_currentVerticalInfo.Descent, descent);

            XUnit singleLineSpace = font.GetHeight();
            RenderInfo imageRenderInfo = CurrentImageRenderInfo;
            if (imageRenderInfo != null)
                singleLineSpace = singleLineSpace - FontHandler.GetAscent(font) + imageRenderInfo.LayoutInfo.ContentArea.Height;

            XUnit inherentLineSpace = Math.Max(_currentVerticalInfo.InherentlineSpace, singleLineSpace);
            switch (spacingRule)
            {
                case LineSpacingRule.Single:
                    lineHeight = singleLineSpace;
                    break;

                case LineSpacingRule.OnePtFive:
                    lineHeight = 1.5 * singleLineSpace;
                    break;

                case LineSpacingRule.Double:
                    lineHeight = 2.0 * singleLineSpace;
                    break;

                case LineSpacingRule.Multiple:
                    lineHeight = _paragraph.Format.LineSpacing * singleLineSpace;
                    break;

                case LineSpacingRule.AtLeast:
                    lineHeight = Math.Max(singleLineSpace, _paragraph.Format.LineSpacing);
                    break;

                case LineSpacingRule.Exactly:
                    lineHeight = new XUnit(_paragraph.Format.LineSpacing);
                    inherentLineSpace = _paragraph.Format.LineSpacing.Point;
                    break;
            }
            lineHeight = Math.Max(_currentVerticalInfo.Height, lineHeight);
            if (MaxElementHeight > 0)
                lineHeight = Math.Min(MaxElementHeight - Tolerance, lineHeight);

            return new VerticalLineInfo(lineHeight, descent, inherentLineSpace);
        }

        /// <summary>
        /// The font used for the current paragraph element.
        /// </summary>
        private XFont CurrentFont
        {
            get { return FontHandler.FontToXFont(CurrentDomFont, /*_documentRenderer.PrivateFonts,*/ _gfx.MUH); }
        }

        private Font CurrentDomFont
        {
            get
            {
                if (_currentLeaf != null)
                {
                    DocumentObject parent = DocumentRelations.GetParent(_currentLeaf.Current);
                    parent = DocumentRelations.GetParent(parent);
                    
                    FormattedText formattedText = parent as FormattedText;
                    if (formattedText != null)
                        return formattedText.Font;
                    
                    Hyperlink hyperlink = parent as Hyperlink;
                    if (hyperlink != null)
                        return hyperlink.Font;
                }
                return _paragraph.Format.Font;
            }
        }

        /// <summary>
        /// Help function to receive a line height on empty paragraphs.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <param name="gfx">The GFX.</param>
        /// <param name="renderer">The renderer.</param>
        public static XUnit GetLineHeight(ParagraphFormat format, XGraphics gfx, DocumentRenderer renderer)
        {
            XFont font = FontHandler.FontToXFont(format.Font, /*renderer.PrivateFonts,*/ gfx.MUH);
            XUnit singleLineSpace = font.GetHeight();
            switch (format.LineSpacingRule)
            {
                case LineSpacingRule.Exactly:
                    return format.LineSpacing.Point;

                case LineSpacingRule.AtLeast:
                    return Math.Max(format.LineSpacing.Point, font.GetHeight());  // old: GetHeight(gfx));

                case LineSpacingRule.Multiple:
                    return format.LineSpacing * format.Font.Size;

                case LineSpacingRule.OnePtFive:
                    return 1.5 * singleLineSpace;

                case LineSpacingRule.Double:
                    return 2.0 * singleLineSpace;

                case LineSpacingRule.Single:
                default:
                    return singleLineSpace;
            }
        }

        void RenderUnderline(XUnit width, bool isWord)
        {
            XPen pen = GetUnderlinePen(isWord);

            bool penChanged = UnderlinePenChanged(pen);
            if (penChanged)
            {
                if (_currentUnderlinePen != null)
                    EndUnderline(_currentUnderlinePen, _currentXPosition);

                if (pen != null)
                    StartUnderline(_currentXPosition);

                _currentUnderlinePen = pen;
            }

            if (_currentLeaf.Current == _endLeaf.Current)
            {
                if (_currentUnderlinePen != null)
                    EndUnderline(_currentUnderlinePen, _currentXPosition + width);

                _currentUnderlinePen = null;
            }
        }

        void StartUnderline(XUnit xPosition)
        {
            _underlineStartPos = xPosition;
        }

        void EndUnderline(XPen pen, XUnit xPosition)
        {
            XUnit yPosition = CurrentBaselinePosition;
            yPosition += 0.33 * _currentVerticalInfo.Descent;
            _gfx.DrawLine(pen, _underlineStartPos, yPosition, xPosition, yPosition);
        }

        XPen _currentUnderlinePen;
        XUnit _underlineStartPos;

        bool UnderlinePenChanged(XPen pen)
        {
            if (pen == null && _currentUnderlinePen == null)
                return false;

            if (pen == null && _currentUnderlinePen != null)
                return true;

            if (pen != null && _currentUnderlinePen == null)
                return true;

            if (pen != null && pen.Color != _currentUnderlinePen.Color)
                return true;

            return pen.Width != _currentUnderlinePen.Width;
        }

        RenderInfo CurrentImageRenderInfo
        {
            get
            {
                if (_currentLeaf != null && _currentLeaf.Current is Image)
                {
                    Image image = (Image)_currentLeaf.Current;
                    if (_imageRenderInfos != null && _imageRenderInfos.ContainsKey(image))
                        return _imageRenderInfos[image];
                    else
                    {
                        if (_imageRenderInfos == null)
                            _imageRenderInfos = new Dictionary<Image, RenderInfo>();

                        RenderInfo renderInfo = CalcImageRenderInfo(image);
                        _imageRenderInfos.Add(image, renderInfo);
                        return renderInfo;
                    }
                }
                return null;
            }
        }
        XPen GetUnderlinePen(bool isWord)
        {
            Font font = CurrentDomFont;
            Underline underlineType = font.Underline;
            if (underlineType == Underline.None)
                return null;

            if (underlineType == Underline.Words && !isWord)
                return null;

#if noCMYK
      XPen pen = new XPen(XColor.FromArgb(font.Color.Argb), font.Size / 16);
#else
            XPen pen = new XPen(ColorHelper.ToXColor(font.Color, _paragraph.Document.UseCmykColor), font.Size / 16);
#endif
            switch (font.Underline)
            {
                case Underline.DotDash:
                    pen.DashStyle = XDashStyle.DashDot;
                    break;

                case Underline.DotDotDash:
                    pen.DashStyle = XDashStyle.DashDotDot;
                    break;

                case Underline.Dash:
                    pen.DashStyle = XDashStyle.Dash;
                    break;

                case Underline.Dotted:
                    pen.DashStyle = XDashStyle.Dot;
                    break;

                case Underline.Single:
                default:
                    pen.DashStyle = XDashStyle.Solid;
                    break;
            }
            return pen;
        }

        private static XStringFormat StringFormat
        {
            get { return _stringFormat ?? (_stringFormat = XStringFormats.Default); }
        }

        /// <summary>
        /// The paragraph to format or render.
        /// </summary>
        readonly Paragraph _paragraph;
        XUnit _currentWordsWidth;
        int _currentBlankCount;
        XUnit _currentLineWidth;
        bool _isFirstLine;
        bool _isLastLine;
        VerticalLineInfo _currentVerticalInfo;
        Area _formattingArea;
        XUnit _currentYPosition;
        XUnit _currentXPosition;
        ParagraphIterator _currentLeaf;
        ParagraphIterator _startLeaf;
        ParagraphIterator _endLeaf;
        static XStringFormat _stringFormat;
        bool _reMeasureLine;
        XUnit _minWidth = 0;
        Dictionary<Image, RenderInfo> _imageRenderInfos;
        List<TabOffset> _tabOffsets;
        DocumentObject _lastTab;
        bool _lastTabPassed;
        XUnit _lastTabPosition;
    }
}
