2546 lines
91 KiB
C#
2546 lines
91 KiB
C#
|
#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;
|
|||
|
}
|
|||
|
}
|