ASCU_ALL/PrintPDF/MigraDoc.Rendering/Rendering/TableRenderer.cs
2021-05-25 17:00:45 +05:00

821 lines
33 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 MigraDoc.DocumentObjectModel.publics;
using PdfSharp.Drawing;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Visitors;
using MigraDoc.DocumentObjectModel.Tables;
namespace MigraDoc.Rendering
{
/// <summary>
/// Renders a table to an XGraphics object.
/// </summary>
public class TableRenderer : Renderer
{
public TableRenderer(XGraphics gfx, Table documentObject, FieldInfos fieldInfos)
: base(gfx, documentObject, fieldInfos)
{
_table = documentObject;
}
public TableRenderer(XGraphics gfx, RenderInfo renderInfo, FieldInfos fieldInfos)
: base(gfx, renderInfo, fieldInfos)
{
_table = (Table)_renderInfo.DocumentObject;
}
public override LayoutInfo InitialLayoutInfo
{
get
{
LayoutInfo layoutInfo = new LayoutInfo();
layoutInfo.KeepTogether = _table.KeepTogether;
layoutInfo.KeepWithNext = false;
layoutInfo.MarginBottom = 0;
layoutInfo.MarginLeft = 0;
layoutInfo.MarginTop = 0;
layoutInfo.MarginRight = 0;
return layoutInfo;
}
}
private void InitRendering()
{
TableFormatInfo formatInfo = (TableFormatInfo)_renderInfo.FormatInfo;
_bottomBorderMap = formatInfo.BottomBorderMap;
_connectedRowsMap = formatInfo.ConnectedRowsMap;
_formattedCells = formatInfo.FormattedCells;
_currRow = formatInfo.StartRow;
_startRow = formatInfo.StartRow;
_endRow = formatInfo.EndRow;
_mergedCells = formatInfo.MergedCells;
_lastHeaderRow = formatInfo.LastHeaderRow;
_startX = _renderInfo.LayoutInfo.ContentArea.X;
_startY = _renderInfo.LayoutInfo.ContentArea.Y;
}
private void RenderHeaderRows()
{
if (_lastHeaderRow < 0)
return;
foreach (Cell cell in _mergedCells)
{
if (cell.Row.Index <= _lastHeaderRow)
RenderCell(cell);
}
}
private void RenderCell(Cell cell)
{
Rectangle innerRect = GetInnerRect(CalcStartingHeight(), cell);
RenderShading(cell, innerRect);
RenderContent(cell, innerRect);
RenderBorders(cell, innerRect);
}
private void EqualizeRoundedCornerBorders(Cell cell)
{
// If any of a corner relevant border is set, we want to copy its values to the second corner relevant border,
// to ensure the innerWidth of the cell is the same, regardless of which border is used.
// If set, we use the vertical borders as source for the values, otherwise we use the horizontal borders.
RoundedCorner roundedCorner = cell.RoundedCorner;
if (roundedCorner == RoundedCorner.None)
return;
BorderType primaryBorderType = BorderType.Top, secondaryBorderType = BorderType.Top;
if (roundedCorner == RoundedCorner.TopLeft || roundedCorner == RoundedCorner.BottomLeft)
primaryBorderType = BorderType.Left;
if (roundedCorner == RoundedCorner.TopRight || roundedCorner == RoundedCorner.BottomRight)
primaryBorderType = BorderType.Right;
if (roundedCorner == RoundedCorner.TopLeft || roundedCorner == RoundedCorner.TopRight)
secondaryBorderType = BorderType.Top;
if (roundedCorner == RoundedCorner.BottomLeft || roundedCorner == RoundedCorner.BottomRight)
secondaryBorderType = BorderType.Bottom;
// If both borders don't exist, there's nothing to do and we should not create one by accessing it.
if (!cell.Borders.HasBorder(primaryBorderType) && !cell.Borders.HasBorder(secondaryBorderType))
return;
// Get the borders. By using GV.ReadWrite we create the border, if not existing.
Border primaryBorder = (Border)cell.Borders.GetValue(primaryBorderType.ToString(), GV.ReadWrite);
Border secondaryBorder = (Border)cell.Borders.GetValue(secondaryBorderType.ToString(), GV.ReadWrite);
Border source = primaryBorder.Visible ? primaryBorder
: secondaryBorder.Visible ? secondaryBorder : null;
Border target = primaryBorder.Visible ? secondaryBorder
: secondaryBorder.Visible ? primaryBorder : null;
if (source == null || target == null)
return;
target.Visible = source.Visible;
target.Width = source.Width;
target.Style = source.Style;
target.Color = source.Color;
}
private void RenderShading(Cell cell, Rectangle innerRect)
{
ShadingRenderer shadeRenderer = new ShadingRenderer(_gfx, cell.Shading);
shadeRenderer.Render(innerRect.X, innerRect.Y, innerRect.Width, innerRect.Height, cell.RoundedCorner);
}
private void RenderBorders(Cell cell, Rectangle innerRect)
{
XUnit leftPos = innerRect.X;
XUnit rightPos = leftPos + innerRect.Width;
XUnit topPos = innerRect.Y;
XUnit bottomPos = innerRect.Y + innerRect.Height;
Borders mergedBorders = _mergedCells.GetEffectiveBorders(cell);
BordersRenderer bordersRenderer = new BordersRenderer(mergedBorders, _gfx);
XUnit bottomWidth = bordersRenderer.GetWidth(BorderType.Bottom);
XUnit leftWidth = bordersRenderer.GetWidth(BorderType.Left);
XUnit topWidth = bordersRenderer.GetWidth(BorderType.Top);
XUnit rightWidth = bordersRenderer.GetWidth(BorderType.Right);
if (cell.RoundedCorner == RoundedCorner.TopLeft)
bordersRenderer.RenderRounded(cell.RoundedCorner, innerRect.X, innerRect.Y, innerRect.Width + rightWidth, innerRect.Height + bottomWidth);
else if (cell.RoundedCorner == RoundedCorner.TopRight)
bordersRenderer.RenderRounded(cell.RoundedCorner, innerRect.X - leftWidth, innerRect.Y, innerRect.Width + leftWidth, innerRect.Height + bottomWidth);
else if (cell.RoundedCorner == RoundedCorner.BottomLeft)
bordersRenderer.RenderRounded(cell.RoundedCorner, innerRect.X, innerRect.Y - topWidth, innerRect.Width + rightWidth, innerRect.Height + topWidth);
else if (cell.RoundedCorner == RoundedCorner.BottomRight)
bordersRenderer.RenderRounded(cell.RoundedCorner, innerRect.X - leftWidth, innerRect.Y - topWidth, innerRect.Width + leftWidth, innerRect.Height + topWidth);
// Render horizontal and vertical borders only if touching no rounded corner.
if (cell.RoundedCorner != RoundedCorner.TopRight && cell.RoundedCorner != RoundedCorner.BottomRight)
bordersRenderer.RenderVertically(BorderType.Right, rightPos, topPos, bottomPos + bottomWidth - topPos);
if (cell.RoundedCorner != RoundedCorner.TopLeft && cell.RoundedCorner != RoundedCorner.BottomLeft)
bordersRenderer.RenderVertically(BorderType.Left, leftPos - leftWidth, topPos, bottomPos + bottomWidth - topPos);
if (cell.RoundedCorner != RoundedCorner.BottomLeft && cell.RoundedCorner != RoundedCorner.BottomRight)
bordersRenderer.RenderHorizontally(BorderType.Bottom, leftPos - leftWidth, bottomPos, rightPos + rightWidth + leftWidth - leftPos);
if (cell.RoundedCorner != RoundedCorner.TopLeft && cell.RoundedCorner != RoundedCorner.TopRight)
bordersRenderer.RenderHorizontally(BorderType.Top, leftPos - leftWidth, topPos - topWidth, rightPos + rightWidth + leftWidth - leftPos);
RenderDiagonalBorders(mergedBorders, innerRect);
}
private void RenderDiagonalBorders(Borders mergedBorders, Rectangle innerRect)
{
BordersRenderer bordersRenderer = new BordersRenderer(mergedBorders, _gfx);
bordersRenderer.RenderDiagonally(BorderType.DiagonalDown, innerRect.X, innerRect.Y, innerRect.Width, innerRect.Height);
bordersRenderer.RenderDiagonally(BorderType.DiagonalUp, innerRect.X, innerRect.Y, innerRect.Width, innerRect.Height);
}
private void RenderContent(Cell cell, Rectangle innerRect)
{
FormattedCell formattedCell = _formattedCells[cell];
RenderInfo[] renderInfos = formattedCell.GetRenderInfos();
if (renderInfos == null)
return;
VerticalAlignment verticalAlignment = cell.VerticalAlignment;
XUnit contentHeight = formattedCell.ContentHeight;
XUnit innerHeight = innerRect.Height;
XUnit targetX = innerRect.X + cell.Column.LeftPadding;
XUnit targetY;
if (verticalAlignment == VerticalAlignment.Bottom)
{
targetY = innerRect.Y + innerRect.Height;
targetY -= cell.Row.BottomPadding;
targetY -= contentHeight;
}
else if (verticalAlignment == VerticalAlignment.Center)
{
targetY = innerRect.Y + cell.Row.TopPadding;
targetY += innerRect.Y + innerRect.Height - cell.Row.BottomPadding;
targetY -= contentHeight;
targetY /= 2;
}
else
targetY = innerRect.Y + cell.Row.TopPadding;
RenderByInfos(targetX, targetY, renderInfos);
}
private Rectangle GetInnerRect(XUnit startingHeight, Cell cell)
{
BordersRenderer bordersRenderer = new BordersRenderer(_mergedCells.GetEffectiveBorders(cell), _gfx);
FormattedCell formattedCell = _formattedCells[cell];
XUnit width = formattedCell.InnerWidth;
XUnit y = _startY;
if (cell.Row.Index > _lastHeaderRow)
y += startingHeight;
else
y += CalcMaxTopBorderWidth(0);
#if true
// !!!new 18-03-09 Attempt to fix an exception. begin
XUnit upperBorderPos;
if (!_bottomBorderMap.TryGetValue(cell.Row.Index, out upperBorderPos))
{
//GetType();
}
// !!!new 18-03-09 Attempt to fix an exception. end
#else
XUnit upperBorderPos = _bottomBorderMap[cell.Row.Index];
#endif
y += upperBorderPos;
if (cell.Row.Index > _lastHeaderRow)
y -= _bottomBorderMap[_startRow];
#if true
// !!!new 18-03-09 Attempt to fix an exception. begin
XUnit lowerBorderPos;
if (!_bottomBorderMap.TryGetValue(cell.Row.Index + cell.MergeDown + 1, out lowerBorderPos))
{
//GetType();
}
// !!!new 18-03-09 Attempt to fix an exception. end
#else
XUnit lowerBorderPos = _bottomBorderMap[cell.Row.Index + cell.MergeDown + 1];
#endif
XUnit height = lowerBorderPos - upperBorderPos;
height -= bordersRenderer.GetWidth(BorderType.Bottom);
XUnit x = _startX;
for (int clmIdx = 0; clmIdx < cell.Column.Index; ++clmIdx)
{
x += _table.Columns[clmIdx].Width;
}
x += LeftBorderOffset;
return new Rectangle(x, y, width, height);
}
public override void Render()
{
InitRendering();
RenderHeaderRows();
if (_startRow < _table.Rows.Count)
{
Cell cell = _table[_startRow, 0];
int cellIdx = _mergedCells.BinarySearch(_table[_startRow, 0], new CellComparer());
while (cellIdx < _mergedCells.Count)
{
cell = _mergedCells[cellIdx];
if (cell.Row.Index > _endRow)
break;
RenderCell(cell);
++cellIdx;
}
}
}
private void InitFormat(Area area, FormatInfo previousFormatInfo)
{
TableFormatInfo prevTableFormatInfo = (TableFormatInfo)previousFormatInfo;
TableRenderInfo tblRenderInfo = new TableRenderInfo();
tblRenderInfo.DocumentObject = _table;
// Equalize the two borders, that are used to determine a rounded corner's border.
// This way the innerWidth of the cell, which is got by the saved _formattedCells, is the same regardless of which corner relevant border is set.
foreach (Row row in _table.Rows)
foreach (Cell cell in row.Cells)
EqualizeRoundedCornerBorders(cell);
_renderInfo = tblRenderInfo;
if (prevTableFormatInfo != null)
{
_mergedCells = prevTableFormatInfo.MergedCells;
_formattedCells = prevTableFormatInfo.FormattedCells;
_bottomBorderMap = prevTableFormatInfo.BottomBorderMap;
_lastHeaderRow = prevTableFormatInfo.LastHeaderRow;
_connectedRowsMap = prevTableFormatInfo.ConnectedRowsMap;
_startRow = prevTableFormatInfo.EndRow + 1;
}
else
{
_mergedCells = new MergedCellList(_table);
FormatCells();
CalcLastHeaderRow();
CreateConnectedRows();
CreateBottomBorderMap();
if (_doHorizontalBreak)
{
CalcLastHeaderColumn();
CreateConnectedColumns();
}
_startRow = _lastHeaderRow + 1;
}
((TableFormatInfo)tblRenderInfo.FormatInfo).MergedCells = _mergedCells;
((TableFormatInfo)tblRenderInfo.FormatInfo).FormattedCells = _formattedCells;
((TableFormatInfo)tblRenderInfo.FormatInfo).BottomBorderMap = _bottomBorderMap;
((TableFormatInfo)tblRenderInfo.FormatInfo).ConnectedRowsMap = _connectedRowsMap;
((TableFormatInfo)tblRenderInfo.FormatInfo).LastHeaderRow = _lastHeaderRow;
}
private void FormatCells()
{
_formattedCells = new Dictionary<Cell, FormattedCell>(); //new Sorted_List(new CellComparer());
foreach (Cell cell in _mergedCells)
{
FormattedCell formattedCell = new FormattedCell(cell, _documentRenderer, _mergedCells.GetEffectiveBorders(cell),
_fieldInfos, 0, 0);
formattedCell.Format(_gfx);
_formattedCells.Add(cell, formattedCell);
}
}
/// <summary>
/// Formats (measures) the table.
/// </summary>
/// <param name="area"> The area on which to fit the table. </param>
/// <param name="previousFormatInfo"> </param>
public override void Format(Area area, FormatInfo previousFormatInfo)
{
DocumentElements elements = DocumentRelations.GetParent(_table) as DocumentElements;
if (elements != null)
{
Section section = DocumentRelations.GetParent(elements) as Section;
if (section != null)
_doHorizontalBreak = section.PageSetup.HorizontalPageBreak;
}
_renderInfo = new TableRenderInfo();
InitFormat(area, previousFormatInfo);
// Don't take any Rows higher then MaxElementHeight
XUnit topHeight = CalcStartingHeight();
XUnit probeHeight = topHeight;
XUnit offset;
if (_startRow > _lastHeaderRow + 1 &&
_startRow < _table.Rows.Count)
offset = _bottomBorderMap[_startRow] - topHeight;
else
offset = -CalcMaxTopBorderWidth(0);
int probeRow = _startRow;
XUnit currentHeight = 0;
XUnit startingHeight = 0;
bool isEmpty = false;
while (probeRow < _table.Rows.Count)
{
bool firstProbe = probeRow == _startRow;
probeRow = _connectedRowsMap[probeRow];
// Don't take any Rows higher then MaxElementHeight
probeHeight = _bottomBorderMap[probeRow + 1] - offset;
// First test whether MaxElementHeight has been set.
if (MaxElementHeight > 0 && firstProbe && probeHeight > MaxElementHeight - Tolerance)
probeHeight = MaxElementHeight - Tolerance;
//if (firstProbe && probeHeight > MaxElementHeight - Tolerance)
// probeHeight = MaxElementHeight - Tolerance;
//The height for the first new row(s) + headerrows:
if (startingHeight == 0)
{
if (probeHeight > area.Height)
{
isEmpty = true;
break;
}
startingHeight = probeHeight;
}
if (probeHeight > area.Height)
break;
else
{
_currRow = probeRow;
currentHeight = probeHeight;
++probeRow;
}
}
if (!isEmpty)
{
TableFormatInfo formatInfo = (TableFormatInfo)_renderInfo.FormatInfo;
formatInfo.StartRow = _startRow;
formatInfo._isEnding = _currRow >= _table.Rows.Count - 1;
formatInfo.EndRow = _currRow;
}
FinishLayoutInfo(area, currentHeight, startingHeight);
}
private void FinishLayoutInfo(Area area, XUnit currentHeight, XUnit startingHeight)
{
LayoutInfo layoutInfo = _renderInfo.LayoutInfo;
layoutInfo.StartingHeight = startingHeight;
//REM: Trailing height would have to be calculated in case tables had a keep with next property.
layoutInfo.TrailingHeight = 0;
if (_currRow >= 0)
{
layoutInfo.ContentArea = new Rectangle(area.X, area.Y, 0, currentHeight);
XUnit width = LeftBorderOffset;
foreach (Column clm in _table.Columns)
{
width += clm.Width;
}
layoutInfo.ContentArea.Width = width;
}
layoutInfo.MinWidth = layoutInfo.ContentArea.Width;
if (!_table.Rows._leftIndent.IsNull)
layoutInfo.Left = _table.Rows.LeftIndent.Point;
else if (_table.Rows.Alignment == RowAlignment.Left)
{
XUnit leftOffset = LeftBorderOffset;
leftOffset += _table.Columns[0].LeftPadding;
layoutInfo.Left = -leftOffset;
}
switch (_table.Rows.Alignment)
{
case RowAlignment.Left:
layoutInfo.HorizontalAlignment = ElementAlignment.Near;
break;
case RowAlignment.Right:
layoutInfo.HorizontalAlignment = ElementAlignment.Far;
break;
case RowAlignment.Center:
layoutInfo.HorizontalAlignment = ElementAlignment.Center;
break;
}
}
private XUnit LeftBorderOffset
{
get
{
if (_leftBorderOffset < 0)
{
if (_table.Rows.Count > 0 && _table.Columns.Count > 0)
{
Borders borders = _mergedCells.GetEffectiveBorders(_table[0, 0]);
BordersRenderer bordersRenderer = new BordersRenderer(borders, _gfx);
_leftBorderOffset = bordersRenderer.GetWidth(BorderType.Left);
}
else
_leftBorderOffset = 0;
}
return _leftBorderOffset;
}
}
private XUnit _leftBorderOffset = -1;
/// <summary>
/// Calcs either the height of the header rows or the height of the uppermost top border.
/// </summary>
/// <returns> </returns>
private XUnit CalcStartingHeight()
{
XUnit height = 0;
if (_lastHeaderRow >= 0)
{
height = _bottomBorderMap[_lastHeaderRow + 1];
height += CalcMaxTopBorderWidth(0);
}
else
{
if (_table.Rows.Count > _startRow)
height = CalcMaxTopBorderWidth(_startRow);
}
return height;
}
private void CalcLastHeaderColumn()
{
_lastHeaderColumn = -1;
foreach (Column clm in _table.Columns)
{
if (clm.HeadingFormat)
_lastHeaderColumn = clm.Index;
else break;
}
if (_lastHeaderColumn >= 0)
_lastHeaderRow = CalcLastConnectedColumn(_lastHeaderColumn);
// Ignore heading format if all the table is heading:
if (_lastHeaderRow == _table.Rows.Count - 1)
_lastHeaderRow = -1;
}
private void CalcLastHeaderRow()
{
_lastHeaderRow = -1;
foreach (Row row in _table.Rows)
{
if (row.HeadingFormat)
_lastHeaderRow = row.Index;
else break;
}
if (_lastHeaderRow >= 0)
_lastHeaderRow = CalcLastConnectedRow(_lastHeaderRow);
// Ignore heading format if all the table is heading:
if (_lastHeaderRow == _table.Rows.Count - 1)
_lastHeaderRow = -1;
}
private void CreateConnectedRows()
{
_connectedRowsMap = new Dictionary<int, int>(); //new Sorted_List();
foreach (Cell cell in _mergedCells)
{
if (!_connectedRowsMap.ContainsKey(cell.Row.Index))
{
int lastConnectedRow = CalcLastConnectedRow(cell.Row.Index);
_connectedRowsMap[cell.Row.Index] = lastConnectedRow;
}
}
}
private void CreateConnectedColumns()
{
_connectedColumnsMap = new Dictionary<int, int>(); //new SortedList();
foreach (Cell cell in _mergedCells)
{
if (!_connectedColumnsMap.ContainsKey(cell.Column.Index))
{
int lastConnectedColumn = CalcLastConnectedColumn(cell.Column.Index);
_connectedColumnsMap[cell.Column.Index] = lastConnectedColumn;
}
}
}
private void CreateBottomBorderMap()
{
_bottomBorderMap = new Dictionary<int, XUnit>(); //new SortedList();
_bottomBorderMap.Add(0, XUnit.FromPoint(0));
while (!_bottomBorderMap.ContainsKey(_table.Rows.Count))
{
CreateNextBottomBorderPosition();
}
}
/// <summary>
/// Calculates the top border width for the first row that is rendered or formatted.
/// </summary>
/// <param name="row"> The row index. </param>
private XUnit CalcMaxTopBorderWidth(int row)
{
XUnit maxWidth = 0;
if (_table.Rows.Count > row)
{
int cellIdx = _mergedCells.BinarySearch(_table[row, 0], new CellComparer());
Cell rowCell = _mergedCells[cellIdx];
while (cellIdx < _mergedCells.Count)
{
rowCell = _mergedCells[cellIdx];
if (rowCell.Row.Index > row)
break;
if (rowCell._borders != null && !rowCell._borders.IsNull())
{
BordersRenderer bordersRenderer = new BordersRenderer(rowCell.Borders, _gfx);
XUnit width = bordersRenderer.GetWidth(BorderType.Top);
if (width > maxWidth)
maxWidth = width;
}
++cellIdx;
}
}
return maxWidth;
}
/// <summary>
/// Creates the next bottom border position.
/// </summary>
private void CreateNextBottomBorderPosition()
{
//int lastIdx = _bottomBorderMap.Count - 1;
// SortedList version:
//int lastBorderRow = (int)bottomBorderMap.GetKey(lastIdx);
//XUnit lastPos = (XUnit)bottomBorderMap.GetByIndex(lastIdx);
int lastBorderRow = 0;
foreach (int key in _bottomBorderMap.Keys)
{
if (key > lastBorderRow)
lastBorderRow = key;
}
XUnit lastPos = _bottomBorderMap[lastBorderRow];
Cell minMergedCell = GetMinMergedCell(lastBorderRow);
FormattedCell minMergedFormattedCell = _formattedCells[minMergedCell];
XUnit maxBottomBorderPosition = lastPos + minMergedFormattedCell.InnerHeight;
maxBottomBorderPosition += CalcBottomBorderWidth(minMergedCell);
foreach (Cell cell in _mergedCells)
{
if (cell.Row.Index > minMergedCell.Row.Index + minMergedCell.MergeDown)
break;
if (cell.Row.Index + cell.MergeDown == minMergedCell.Row.Index + minMergedCell.MergeDown)
{
FormattedCell formattedCell = _formattedCells[cell];
// !!!new 18-03-09 Attempt to fix an exception. begin
// if (cell.Row.Index < _bottomBorderMap.Count)
{
// !!!new 18-03-09 Attempt to fix an exception. end
#if true
// !!!new 18-03-09 Attempt to fix an exception. begin
XUnit topBorderPos = maxBottomBorderPosition;
if (!_bottomBorderMap.TryGetValue(cell.Row.Index, out topBorderPos))
{
//GetType();
}
// !!!new 18-03-09 Attempt to fix an exception. end
#else
XUnit topBorderPos = _bottomBorderMap[cell.Row.Index];
#endif
XUnit bottomBorderPos = topBorderPos + formattedCell.InnerHeight;
bottomBorderPos += CalcBottomBorderWidth(cell);
if (bottomBorderPos > maxBottomBorderPosition)
maxBottomBorderPosition = bottomBorderPos;
// !!!new 18-03-09 Attempt to fix an exception. begin
}
// !!!new 18-03-09 Attempt to fix an exception. end
}
}
_bottomBorderMap.Add(minMergedCell.Row.Index + minMergedCell.MergeDown + 1, maxBottomBorderPosition);
}
/// <summary>
/// Calculates bottom border width of a cell.
/// </summary>
/// <param name="cell"> The cell the bottom border of the row that is probed. </param>
/// <returns> The calculated border width. </returns>
private XUnit CalcBottomBorderWidth(Cell cell)
{
Borders borders = _mergedCells.GetEffectiveBorders(cell);
if (borders != null)
{
BordersRenderer bordersRenderer = new BordersRenderer(borders, _gfx);
return bordersRenderer.GetWidth(BorderType.Bottom);
}
return 0;
}
/// <summary>
/// Gets the first cell that ends in the given row or as close as possible.
/// </summary>
/// <param name="row">The row to probe.</param>
/// <returns>The first cell with minimal vertical merge.</returns>
private Cell GetMinMergedCell(int row)
{
#if true
//!!!new 18-03-10 begin
// Also look at rows above "row", but only consider cells that end at "row" or as close as possible.
int minMerge = _table.Rows.Count;
Cell minCell = null;
foreach (Cell cell in _mergedCells)
{
if (cell.Row.Index <= row && cell.Row.Index + cell.MergeDown >= row)
{
if (cell.Row.Index == row && cell.MergeDown == 0)
{
// Perfect match: non-merged cell in the desired row.
minCell = cell;
break;
}
else if (cell.Row.Index + cell.MergeDown - row < minMerge)
{
minMerge = cell.Row.Index + cell.MergeDown - row;
minCell = cell;
}
}
else if (cell.Row.Index > row)
break;
}
//!!!new 18-03-10 end
#else
int minMerge = _table.Rows.Count;
Cell minCell = null;
foreach (Cell cell in _mergedCells)
{
if (cell.Row.Index == row)
{
if (cell.MergeDown == 0)
{
minCell = cell;
break;
}
else if (cell.MergeDown < minMerge)
{
minMerge = cell.MergeDown;
minCell = cell;
}
}
else if (cell.Row.Index > row)
break;
}
#endif
return minCell;
}
/// <summary>
/// Calculates the last row that is connected with the given row.
/// </summary>
/// <param name="row"> The row that is probed for downward connection. </param>
/// <returns> The last row that is connected with the given row. </returns>
private int CalcLastConnectedRow(int row)
{
int lastConnectedRow = row;
foreach (Cell cell in _mergedCells)
{
if (cell.Row.Index <= lastConnectedRow)
{
int downConnection = Math.Max(cell.Row.KeepWith, cell.MergeDown);
if (lastConnectedRow < cell.Row.Index + downConnection)
lastConnectedRow = cell.Row.Index + downConnection;
}
}
return lastConnectedRow;
}
/// <summary>
/// Calculates the last column that is connected with the specified column.
/// </summary>
/// <param name="column"> The column that is probed for downward connection. </param>
/// <returns> The last column that is connected with the given column. </returns>
private int CalcLastConnectedColumn(int column)
{
int lastConnectedColumn = column;
foreach (Cell cell in _mergedCells)
{
if (cell.Column.Index <= lastConnectedColumn)
{
int rightConnection = Math.Max(cell.Column.KeepWith, cell.MergeRight);
if (lastConnectedColumn < cell.Column.Index + rightConnection)
lastConnectedColumn = cell.Column.Index + rightConnection;
}
}
return lastConnectedColumn;
}
private readonly Table _table;
private MergedCellList _mergedCells;
private Dictionary<Cell, FormattedCell> _formattedCells; //SortedList formattedCells;
private Dictionary<int, XUnit> _bottomBorderMap; //SortedList bottomBorderMap;
private Dictionary<int, int> _connectedRowsMap; //SortedList connectedRowsMap;
private Dictionary<int, int> _connectedColumnsMap; //SortedList connectedColumnsMap;
private int _lastHeaderRow;
private int _lastHeaderColumn;
private int _startRow;
private int _currRow;
private int _endRow = -1;
private bool _doHorizontalBreak;
private XUnit _startX;
private XUnit _startY;
}
}