725 lines
27 KiB
C#
725 lines
27 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.Globalization;
|
||
|
using PdfSharp;
|
||
|
using PdfSharp.Drawing;
|
||
|
using MigraDoc.DocumentObjectModel;
|
||
|
using MigraDoc.DocumentObjectModel.publics;
|
||
|
|
||
|
namespace MigraDoc.Rendering
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represents a formatted document.
|
||
|
/// </summary>
|
||
|
public class FormattedDocument : IAreaProvider
|
||
|
{
|
||
|
enum PagePosition
|
||
|
{
|
||
|
First,
|
||
|
Odd,
|
||
|
Even
|
||
|
}
|
||
|
|
||
|
private struct HeaderFooterPosition
|
||
|
{
|
||
|
public HeaderFooterPosition(int sectionNr, PagePosition pagePosition)
|
||
|
{
|
||
|
_sectionNr = sectionNr;
|
||
|
_pagePosition = pagePosition;
|
||
|
}
|
||
|
|
||
|
public override bool Equals(object obj)
|
||
|
{
|
||
|
if (obj is HeaderFooterPosition)
|
||
|
{
|
||
|
HeaderFooterPosition hfp = (HeaderFooterPosition)obj;
|
||
|
return _sectionNr == hfp._sectionNr && _pagePosition == hfp._pagePosition;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public override int GetHashCode()
|
||
|
{
|
||
|
return _sectionNr.GetHashCode() ^ _pagePosition.GetHashCode();
|
||
|
}
|
||
|
|
||
|
readonly int _sectionNr;
|
||
|
readonly PagePosition _pagePosition;
|
||
|
}
|
||
|
|
||
|
public FormattedDocument(Document document, DocumentRenderer documentRenderer)
|
||
|
{
|
||
|
_document = document;
|
||
|
_documentRenderer = documentRenderer;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Formats the document by performing line breaks and page breaks.
|
||
|
/// </summary>
|
||
|
public void Format(XGraphics gfx)
|
||
|
{
|
||
|
_bookmarks = new Dictionary<string, FieldInfos.BookmarkInfo>();
|
||
|
_pageRenderInfos = new Dictionary<int, List<RenderInfo>>();
|
||
|
_pageInfos = new Dictionary<int, PageInfo>();
|
||
|
_pageFieldInfos = new Dictionary<int, FieldInfos>();
|
||
|
_formattedHeaders = new Dictionary<HeaderFooterPosition, FormattedHeaderFooter>();
|
||
|
_formattedFooters = new Dictionary<HeaderFooterPosition, FormattedHeaderFooter>();
|
||
|
_gfx = gfx;
|
||
|
_currentPage = 0;
|
||
|
_sectionNumber = 0;
|
||
|
_pageCount = 0;
|
||
|
_shownPageNumber = 0;
|
||
|
_documentRenderer.ProgressCompleted = 0;
|
||
|
_documentRenderer.ProgressMaximum = 0;
|
||
|
if (_documentRenderer.HasPrepareDocumentProgress)
|
||
|
{
|
||
|
foreach (Section section in _document.Sections)
|
||
|
_documentRenderer.ProgressMaximum += section.Elements.Count;
|
||
|
}
|
||
|
foreach (Section section in _document.Sections)
|
||
|
{
|
||
|
_isNewSection = true;
|
||
|
_currentSection = section;
|
||
|
++_sectionNumber;
|
||
|
if (NeedsEmptyPage())
|
||
|
InsertEmptyPage();
|
||
|
|
||
|
TopDownFormatter formatter = new TopDownFormatter(this, _documentRenderer, section.Elements);
|
||
|
formatter.FormatOnAreas(gfx, true);
|
||
|
FillSectionPagesInfo();
|
||
|
_documentRenderer.ProgressCompleted += section.Elements.Count;
|
||
|
}
|
||
|
_pageCount = _currentPage;
|
||
|
FillNumPagesInfo();
|
||
|
}
|
||
|
|
||
|
PagePosition CurrentPagePosition
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (_isNewSection)
|
||
|
return PagePosition.First;
|
||
|
// Choose header and footer based on the shown page number, not the physical page number.
|
||
|
if (_shownPageNumber % 2 == 0)
|
||
|
return PagePosition.Even;
|
||
|
return PagePosition.Odd;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FormatHeadersFooters()
|
||
|
{
|
||
|
HeadersFooters headers = (HeadersFooters)_currentSection.GetValue("Headers", GV.ReadOnly);
|
||
|
if (headers != null)
|
||
|
{
|
||
|
PagePosition pagePos = CurrentPagePosition;
|
||
|
HeaderFooterPosition hfp = new HeaderFooterPosition(_sectionNumber, pagePos);
|
||
|
if (!_formattedHeaders.ContainsKey(hfp))
|
||
|
FormatHeader(hfp, ChooseHeaderFooter(headers, pagePos));
|
||
|
}
|
||
|
|
||
|
HeadersFooters footers = (HeadersFooters)_currentSection.GetValue("Footers", GV.ReadOnly);
|
||
|
if (footers != null)
|
||
|
{
|
||
|
PagePosition pagePos = CurrentPagePosition;
|
||
|
HeaderFooterPosition hfp = new HeaderFooterPosition(_sectionNumber, pagePos);
|
||
|
if (!_formattedFooters.ContainsKey(hfp))
|
||
|
FormatFooter(hfp, ChooseHeaderFooter(footers, pagePos));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void FormatHeader(HeaderFooterPosition hfp, HeaderFooter header)
|
||
|
{
|
||
|
if (header != null && !_formattedHeaders.ContainsKey(hfp))
|
||
|
{
|
||
|
FormattedHeaderFooter formattedHeaderFooter = new FormattedHeaderFooter(header, _documentRenderer, _currentFieldInfos);
|
||
|
formattedHeaderFooter.ContentRect = GetHeaderArea(_currentSection, _currentPage);
|
||
|
formattedHeaderFooter.Format(_gfx);
|
||
|
_formattedHeaders.Add(hfp, formattedHeaderFooter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void FormatFooter(HeaderFooterPosition hfp, HeaderFooter footer)
|
||
|
{
|
||
|
if (footer != null && !_formattedFooters.ContainsKey(hfp))
|
||
|
{
|
||
|
FormattedHeaderFooter formattedHeaderFooter = new FormattedHeaderFooter(footer, _documentRenderer, _currentFieldInfos);
|
||
|
formattedHeaderFooter.ContentRect = GetFooterArea(_currentSection, _currentPage);
|
||
|
formattedHeaderFooter.Format(_gfx);
|
||
|
_formattedFooters.Add(hfp, formattedHeaderFooter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Fills the number pages information after formatting the document.
|
||
|
/// </summary>
|
||
|
void FillNumPagesInfo()
|
||
|
{
|
||
|
for (int page = 1; page <= _pageCount; ++page)
|
||
|
{
|
||
|
if (IsEmptyPage(page))
|
||
|
continue;
|
||
|
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
fieldInfos.NumPages = _pageCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Fills the section pages information after formatting a section.
|
||
|
/// </summary>
|
||
|
void FillSectionPagesInfo()
|
||
|
{
|
||
|
for (int page = _currentPage; page > 0; --page)
|
||
|
{
|
||
|
if (IsEmptyPage(page))
|
||
|
continue;
|
||
|
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
if (fieldInfos.Section != _sectionNumber)
|
||
|
break;
|
||
|
|
||
|
fieldInfos.SectionPages = _sectionPages;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Rectangle CalcContentRect(int page)
|
||
|
{
|
||
|
PageSetup pageSetup = _currentSection.PageSetup;
|
||
|
XUnit width = pageSetup.EffectivePageWidth.Point;
|
||
|
|
||
|
width -= pageSetup.RightMargin.Point;
|
||
|
width -= pageSetup.LeftMargin.Point;
|
||
|
|
||
|
XUnit height = pageSetup.EffectivePageHeight.Point;
|
||
|
|
||
|
height -= pageSetup.TopMargin.Point;
|
||
|
height -= pageSetup.BottomMargin.Point;
|
||
|
XUnit x;
|
||
|
XUnit y = pageSetup.TopMargin.Point;
|
||
|
if (pageSetup.MirrorMargins)
|
||
|
x = page % 2 == 0 ? pageSetup.RightMargin.Point : pageSetup.LeftMargin.Point;
|
||
|
else
|
||
|
x = pageSetup.LeftMargin.Point;
|
||
|
return new Rectangle(x, y, width, height);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the rendering informations for the page content.
|
||
|
/// </summary>
|
||
|
/// <param name="page">The page to render.</param>
|
||
|
/// <returns>Rendering information for the page content.</returns>
|
||
|
public RenderInfo[] GetRenderInfos(int page)
|
||
|
{
|
||
|
if (_pageRenderInfos.ContainsKey(page))
|
||
|
return (_pageRenderInfos[page]).ToArray();
|
||
|
return null;
|
||
|
}
|
||
|
private Dictionary<int, List<RenderInfo>> _pageRenderInfos;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a formatted headerfooter object for header of the given page.
|
||
|
/// </summary>
|
||
|
/// <param name="page">The physical page the header shall appear on.</param>
|
||
|
/// <returns>The required header, null if none exists to render.</returns>
|
||
|
public FormattedHeaderFooter GetFormattedHeader(int page)
|
||
|
{
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
int logicalPage = fieldInfos.DisplayPageNr;
|
||
|
|
||
|
PagePosition pagePos = logicalPage % 2 == 0 ? PagePosition.Even : PagePosition.Odd;
|
||
|
|
||
|
if (page == 1)
|
||
|
pagePos = PagePosition.First;
|
||
|
else //page > 1
|
||
|
{
|
||
|
if (IsEmptyPage(page - 1)) // these empty pages only occur between sections.
|
||
|
pagePos = PagePosition.First;
|
||
|
else
|
||
|
{
|
||
|
FieldInfos prevFieldInfos = _pageFieldInfos[page - 1];
|
||
|
if (fieldInfos.Section != prevFieldInfos.Section)
|
||
|
pagePos = PagePosition.First;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HeaderFooterPosition hfp = new HeaderFooterPosition(fieldInfos.Section, pagePos);
|
||
|
if (_formattedHeaders.ContainsKey(hfp))
|
||
|
return _formattedHeaders[hfp];
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a formatted headerfooter object for footer of the given page.
|
||
|
/// </summary>
|
||
|
/// <param name="page">The physical page the footer shall appear on.</param>
|
||
|
/// <returns>The required footer, null if none exists to render.</returns>
|
||
|
public FormattedHeaderFooter GetFormattedFooter(int page)
|
||
|
{
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
int logicalPage = fieldInfos.DisplayPageNr;
|
||
|
|
||
|
PagePosition pagePos = logicalPage % 2 == 0 ? PagePosition.Even : PagePosition.Odd;
|
||
|
|
||
|
if (page == 1)
|
||
|
pagePos = PagePosition.First;
|
||
|
else //page > 1
|
||
|
{
|
||
|
if (IsEmptyPage(page - 1)) // these empty pages only occur between sections.
|
||
|
pagePos = PagePosition.First;
|
||
|
else
|
||
|
{
|
||
|
FieldInfos prevFieldInfos = _pageFieldInfos[page - 1];
|
||
|
if (fieldInfos.Section != prevFieldInfos.Section)
|
||
|
pagePos = PagePosition.First;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HeaderFooterPosition hfp = new HeaderFooterPosition(fieldInfos.Section, pagePos);
|
||
|
if (_formattedFooters.ContainsKey(hfp))
|
||
|
return _formattedFooters[hfp];
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private Rectangle GetHeaderArea(Section section, int page)
|
||
|
{
|
||
|
PageSetup pageSetup = section.PageSetup;
|
||
|
XUnit xPos;
|
||
|
if (pageSetup.MirrorMargins && page % 2 == 0)
|
||
|
xPos = pageSetup.RightMargin.Point;
|
||
|
else
|
||
|
xPos = pageSetup.LeftMargin.Point;
|
||
|
|
||
|
XUnit width = pageSetup.EffectivePageWidth.Point;
|
||
|
width -= pageSetup.LeftMargin + pageSetup.RightMargin;
|
||
|
|
||
|
XUnit yPos = pageSetup.HeaderDistance.Point;
|
||
|
XUnit height = pageSetup.TopMargin - pageSetup.HeaderDistance;
|
||
|
return new Rectangle(xPos, yPos, width, height);
|
||
|
}
|
||
|
|
||
|
public Rectangle GetHeaderArea(int page)
|
||
|
{
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
Section section = _document.Sections[fieldInfos.Section - 1];
|
||
|
return GetHeaderArea(section, page);
|
||
|
}
|
||
|
|
||
|
public Rectangle GetFooterArea(int page)
|
||
|
{
|
||
|
FieldInfos fieldInfos = _pageFieldInfos[page];
|
||
|
Section section = _document.Sections[fieldInfos.Section - 1];
|
||
|
return GetFooterArea(section, page);
|
||
|
}
|
||
|
|
||
|
private Rectangle GetFooterArea(Section section, int page)
|
||
|
{
|
||
|
PageSetup pageSetup = section.PageSetup;
|
||
|
XUnit xPos;
|
||
|
if (pageSetup.MirrorMargins && page % 2 == 0)
|
||
|
xPos = pageSetup.RightMargin.Point;
|
||
|
else
|
||
|
xPos = pageSetup.LeftMargin.Point;
|
||
|
|
||
|
XUnit width = pageSetup.EffectivePageWidth.Point;
|
||
|
width -= pageSetup.LeftMargin + pageSetup.RightMargin;
|
||
|
XUnit yPos = pageSetup.EffectivePageHeight.Point;
|
||
|
|
||
|
yPos -= pageSetup.BottomMargin.Point;
|
||
|
XUnit height = pageSetup.BottomMargin - pageSetup.FooterDistance;
|
||
|
return new Rectangle(xPos, yPos, width, height);
|
||
|
}
|
||
|
|
||
|
private HeaderFooter ChooseHeaderFooter(HeadersFooters hfs, PagePosition pagePos)
|
||
|
{
|
||
|
if (hfs == null)
|
||
|
return null;
|
||
|
|
||
|
PageSetup pageSetup = _currentSection.PageSetup;
|
||
|
|
||
|
if (pagePos == PagePosition.First)
|
||
|
{
|
||
|
if (pageSetup.DifferentFirstPageHeaderFooter)
|
||
|
return (HeaderFooter)hfs.GetValue("FirstPage", GV.ReadOnly);
|
||
|
}
|
||
|
if (pagePos == PagePosition.Even || _shownPageNumber/*_currentPage*/ % 2 == 0)
|
||
|
{
|
||
|
if (pageSetup.OddAndEvenPagesHeaderFooter)
|
||
|
return (HeaderFooter)hfs.GetValue("EvenPage", GV.ReadOnly);
|
||
|
}
|
||
|
return (HeaderFooter)hfs.GetValue("Primary", GV.ReadOnly);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the number of pages of the document.
|
||
|
/// </summary>
|
||
|
public int PageCount
|
||
|
{
|
||
|
get { return _pageCount; }
|
||
|
}
|
||
|
int _pageCount;
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets information about the specified page.
|
||
|
/// </summary>
|
||
|
/// <param name="page">The page the information is asked for.</param>
|
||
|
/// <returns>The page information.</returns>
|
||
|
public PageInfo GetPageInfo(int page)
|
||
|
{
|
||
|
if (page < 1 || page > _pageCount)
|
||
|
#if !SILVERLIGHT
|
||
|
throw new ArgumentOutOfRangeException("page", page, page.ToString(CultureInfo.InvariantCulture));
|
||
|
#else
|
||
|
throw new PdfSharp.ArgumentOutOfRangeException("page", page, page.ToString(CultureInfo.InvariantCulture));
|
||
|
#endif
|
||
|
|
||
|
return _pageInfos[page];
|
||
|
}
|
||
|
|
||
|
#region IAreaProvider Members
|
||
|
|
||
|
Area IAreaProvider.GetNextArea()
|
||
|
{
|
||
|
if (_isNewSection)
|
||
|
_sectionPages = 0;
|
||
|
|
||
|
++_currentPage;
|
||
|
++_shownPageNumber;
|
||
|
++_sectionPages;
|
||
|
InitFieldInfos();
|
||
|
FormatHeadersFooters();
|
||
|
_isNewSection = false;
|
||
|
return CalcContentRect(_currentPage);
|
||
|
}
|
||
|
int _currentPage;
|
||
|
|
||
|
Area IAreaProvider.ProbeNextArea()
|
||
|
{
|
||
|
return CalcContentRect(_currentPage + 1);
|
||
|
}
|
||
|
|
||
|
void InitFieldInfos()
|
||
|
{
|
||
|
_currentFieldInfos = new FieldInfos(_bookmarks);
|
||
|
_currentFieldInfos.PhysicalPageNr = _currentPage;
|
||
|
_currentFieldInfos.Section = _sectionNumber;
|
||
|
|
||
|
if (_isNewSection && !_currentSection.PageSetup._startingNumber.IsNull)
|
||
|
_shownPageNumber = _currentSection.PageSetup.StartingNumber;
|
||
|
|
||
|
_currentFieldInfos.DisplayPageNr = _shownPageNumber;
|
||
|
}
|
||
|
|
||
|
void IAreaProvider.StoreRenderInfos(List<RenderInfo> renderInfos)
|
||
|
{
|
||
|
_pageRenderInfos.Add(_currentPage, renderInfos);
|
||
|
XSize pageSize = CalcPageSize(_currentSection.PageSetup);
|
||
|
PageOrientation pageOrientation = CalcPageOrientation(_currentSection.PageSetup);
|
||
|
PageInfo pageInfo = new PageInfo(pageSize.Width, pageSize.Height, pageOrientation);
|
||
|
_pageInfos.Add(_currentPage, pageInfo);
|
||
|
_pageFieldInfos.Add(_currentPage, _currentFieldInfos);
|
||
|
}
|
||
|
|
||
|
PageOrientation CalcPageOrientation(PageSetup pageSetup)
|
||
|
{
|
||
|
PageOrientation pageOrientation = PageOrientation.Portrait;
|
||
|
if (_currentSection.PageSetup.Orientation == Orientation.Landscape)
|
||
|
pageOrientation = PageOrientation.Landscape;
|
||
|
|
||
|
return pageOrientation;
|
||
|
}
|
||
|
|
||
|
XSize CalcPageSize(PageSetup pageSetup)
|
||
|
{
|
||
|
return new XSize(pageSetup.PageWidth.Point, pageSetup.PageHeight.Point);
|
||
|
}
|
||
|
|
||
|
bool IAreaProvider.PositionHorizontally(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
switch (layoutInfo.HorizontalReference)
|
||
|
{
|
||
|
case HorizontalReference.PageMargin:
|
||
|
case HorizontalReference.AreaBoundary:
|
||
|
return PositionHorizontallyToMargin(layoutInfo);
|
||
|
|
||
|
case HorizontalReference.Page:
|
||
|
return PositionHorizontallyToPage(layoutInfo);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the alignment depending on the currentPage for the alignments "Outside" and "Inside".
|
||
|
/// </summary>
|
||
|
/// <param name="alignment">The original alignment</param>
|
||
|
/// <returns>the alignment depending on the currentPage for the alignments "Outside" and "Inside"</returns>
|
||
|
private ElementAlignment GetCurrentAlignment(ElementAlignment alignment)
|
||
|
{
|
||
|
ElementAlignment align = alignment;
|
||
|
|
||
|
if (align == ElementAlignment.Inside)
|
||
|
{
|
||
|
align = _currentPage % 2 == 0 ? ElementAlignment.Far : ElementAlignment.Near;
|
||
|
}
|
||
|
else if (align == ElementAlignment.Outside)
|
||
|
{
|
||
|
align = _currentPage % 2 == 0 ? ElementAlignment.Near : ElementAlignment.Far;
|
||
|
}
|
||
|
return align;
|
||
|
}
|
||
|
|
||
|
bool PositionHorizontallyToMargin(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
Rectangle rect = CalcContentRect(_currentPage);
|
||
|
ElementAlignment align = GetCurrentAlignment(layoutInfo.HorizontalAlignment);
|
||
|
|
||
|
|
||
|
switch (align)
|
||
|
{
|
||
|
case ElementAlignment.Near:
|
||
|
if (layoutInfo.Left != 0)
|
||
|
{
|
||
|
layoutInfo.ContentArea.X += layoutInfo.Left;
|
||
|
return true;
|
||
|
}
|
||
|
if (layoutInfo.MarginLeft != 0)
|
||
|
{
|
||
|
layoutInfo.ContentArea.X += layoutInfo.MarginLeft;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
case ElementAlignment.Far:
|
||
|
XUnit xPos = rect.X + rect.Width;
|
||
|
xPos -= layoutInfo.ContentArea.Width;
|
||
|
xPos -= layoutInfo.MarginRight;
|
||
|
layoutInfo.ContentArea.X = xPos;
|
||
|
return true;
|
||
|
|
||
|
case ElementAlignment.Center:
|
||
|
xPos = rect.Width;
|
||
|
xPos -= layoutInfo.ContentArea.Width;
|
||
|
xPos = rect.X + xPos / 2;
|
||
|
layoutInfo.ContentArea.X = xPos;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool PositionHorizontallyToPage(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
XUnit xPos;
|
||
|
ElementAlignment align = GetCurrentAlignment(layoutInfo.HorizontalAlignment);
|
||
|
switch (align)
|
||
|
{
|
||
|
case ElementAlignment.Near:
|
||
|
#if true
|
||
|
// Attempt to make it compatible with MigraDoc CPP.
|
||
|
// Ignore layoutInfo.Left if absolute position is specified in layoutInfo.MarginLeft.
|
||
|
// Use layoutInfo.Left if layoutInfo.MarginLeft is 0.
|
||
|
// TODO We would need HasValue for XUnit to determine whether a value was assigned.
|
||
|
if (layoutInfo.HorizontalReference == HorizontalReference.Page ||
|
||
|
layoutInfo.HorizontalReference == HorizontalReference.PageMargin)
|
||
|
xPos = layoutInfo.MarginLeft != 0 ? layoutInfo.MarginLeft : layoutInfo.Left;
|
||
|
else
|
||
|
xPos = Math.Max(layoutInfo.MarginLeft, layoutInfo.Left);
|
||
|
#else
|
||
|
if (layoutInfo.HorizontalReference == HorizontalReference.Page ||
|
||
|
layoutInfo.HorizontalReference == HorizontalReference.PageMargin)
|
||
|
xPos = layoutInfo.MarginLeft; // ignore layoutInfo.Left if absolute position is specified
|
||
|
else
|
||
|
xPos = Math.Max(layoutInfo.MarginLeft, layoutInfo.Left);
|
||
|
#endif
|
||
|
layoutInfo.ContentArea.X = xPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Far:
|
||
|
xPos = _currentSection.PageSetup.EffectivePageWidth.Point;
|
||
|
xPos -= layoutInfo.ContentArea.Width;
|
||
|
xPos -= layoutInfo.MarginRight;
|
||
|
layoutInfo.ContentArea.X = xPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Center:
|
||
|
xPos = _currentSection.PageSetup.EffectivePageWidth.Point;
|
||
|
xPos -= layoutInfo.ContentArea.Width;
|
||
|
xPos /= 2;
|
||
|
layoutInfo.ContentArea.X = xPos;
|
||
|
break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool PositionVerticallyToMargin(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
Rectangle rect = CalcContentRect(_currentPage);
|
||
|
XUnit yPos;
|
||
|
switch (layoutInfo.VerticalAlignment)
|
||
|
{
|
||
|
case ElementAlignment.Near:
|
||
|
yPos = rect.Y;
|
||
|
if (layoutInfo.Top == 0)
|
||
|
yPos += layoutInfo.MarginTop;
|
||
|
else
|
||
|
yPos += layoutInfo.Top;
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Far:
|
||
|
yPos = rect.Y + rect.Height;
|
||
|
yPos -= layoutInfo.ContentArea.Height;
|
||
|
yPos -= layoutInfo.MarginBottom;
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Center:
|
||
|
yPos = rect.Height;
|
||
|
yPos -= layoutInfo.ContentArea.Height;
|
||
|
yPos = rect.Y + yPos / 2;
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool NeedsEmptyPage()
|
||
|
{
|
||
|
int nextPage = _currentPage + 1;
|
||
|
PageSetup pageSetup = _currentSection.PageSetup;
|
||
|
bool startOnEvenPage = pageSetup.SectionStart == BreakType.BreakEvenPage;
|
||
|
bool startOnOddPage = pageSetup.SectionStart == BreakType.BreakOddPage;
|
||
|
|
||
|
if (startOnOddPage)
|
||
|
return nextPage % 2 == 0;
|
||
|
if (startOnEvenPage)
|
||
|
return nextPage % 2 == 1;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void InsertEmptyPage()
|
||
|
{
|
||
|
++_currentPage;
|
||
|
++_shownPageNumber;
|
||
|
_emptyPages.Add(_currentPage, null);
|
||
|
|
||
|
XSize pageSize = CalcPageSize(_currentSection.PageSetup);
|
||
|
PageOrientation pageOrientation = CalcPageOrientation(_currentSection.PageSetup);
|
||
|
PageInfo pageInfo = new PageInfo(pageSize.Width, pageSize.Height, pageOrientation);
|
||
|
_pageInfos.Add(_currentPage, pageInfo);
|
||
|
}
|
||
|
|
||
|
bool PositionVerticallyToPage(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
XUnit yPos;
|
||
|
switch (layoutInfo.VerticalAlignment)
|
||
|
{
|
||
|
case ElementAlignment.Near:
|
||
|
yPos = Math.Max(layoutInfo.MarginTop, layoutInfo.Top);
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Far:
|
||
|
yPos = _currentSection.PageSetup.EffectivePageHeight.Point;
|
||
|
yPos -= layoutInfo.ContentArea.Height;
|
||
|
yPos -= layoutInfo.MarginBottom;
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
|
||
|
case ElementAlignment.Center:
|
||
|
yPos = _currentSection.PageSetup.EffectivePageHeight.Point;
|
||
|
yPos -= layoutInfo.ContentArea.Height;
|
||
|
yPos /= 2;
|
||
|
layoutInfo.ContentArea.Y = yPos;
|
||
|
break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool IAreaProvider.PositionVertically(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
switch (layoutInfo.VerticalReference)
|
||
|
{
|
||
|
case VerticalReference.PreviousElement:
|
||
|
return false;
|
||
|
|
||
|
case VerticalReference.AreaBoundary:
|
||
|
case VerticalReference.PageMargin:
|
||
|
return PositionVerticallyToMargin(layoutInfo);
|
||
|
|
||
|
case VerticalReference.Page:
|
||
|
return PositionVerticallyToPage(layoutInfo);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public FieldInfos GetFieldInfos(int page)
|
||
|
{
|
||
|
return _pageFieldInfos[page];
|
||
|
}
|
||
|
|
||
|
FieldInfos IAreaProvider.AreaFieldInfos
|
||
|
{
|
||
|
get { return _currentFieldInfos; }
|
||
|
}
|
||
|
|
||
|
bool IAreaProvider.IsAreaBreakBefore(LayoutInfo layoutInfo)
|
||
|
{
|
||
|
return layoutInfo.PageBreakBefore;
|
||
|
}
|
||
|
|
||
|
public bool IsEmptyPage(int page)
|
||
|
{
|
||
|
return _emptyPages.ContainsKey(page);
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
Dictionary<string, FieldInfos.BookmarkInfo> _bookmarks;
|
||
|
int _sectionPages;
|
||
|
int _shownPageNumber;
|
||
|
int _sectionNumber;
|
||
|
Section _currentSection;
|
||
|
bool _isNewSection;
|
||
|
FieldInfos _currentFieldInfos;
|
||
|
Dictionary<int, FieldInfos> _pageFieldInfos;
|
||
|
Dictionary<HeaderFooterPosition, FormattedHeaderFooter> _formattedHeaders;
|
||
|
Dictionary<HeaderFooterPosition, FormattedHeaderFooter> _formattedFooters;
|
||
|
readonly DocumentRenderer _documentRenderer;
|
||
|
XGraphics _gfx;
|
||
|
Dictionary<int, PageInfo> _pageInfos;
|
||
|
readonly Dictionary<int, object> _emptyPages = new Dictionary<int, object>();
|
||
|
readonly Document _document;
|
||
|
}
|
||
|
}
|