/*
 * (c) Copyright Ascensio System SIA 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";
CTable.prototype.Recalculate_Page = function(PageIndex)
{
	if (0 === PageIndex)
	{
		// TODO: Внутри функции private_RecalculateBorders происходит персчет метрик каждой ячейки, это надо бы
		//       вынести в отдельную функцию. Из-за этого функцию private_RecalculateHeader приходится запускать позже.

		this.private_RecalculateGrid();
		this.private_RecalculateBorders();
		this.private_RecalculateHeader();
	}

	this.private_RecalculatePageXY(PageIndex);

	if (true !== this.private_RecalculateCheckPageColumnBreak(PageIndex))
		return recalcresult_NextPage | recalcresultflags_Column;

	this.private_RecalculatePositionX(PageIndex);

	var Result = this.private_RecalculatePage(PageIndex);
	if (Result & recalcresult_CurPage)
		return Result;

	this.private_RecalculatePositionY(PageIndex);

	if (Result & recalcresult_NextElement)
		this.RecalcInfo.Reset(false);

	return Result;
};
CTable.prototype.Recalculate_SkipPage = function(PageIndex)
{
	if (0 === PageIndex)
	{
		this.StartFromNewPage();
	}
	else
	{
		var PrevPage = this.Pages[PageIndex - 1];

		var LastRow      = Math.max(PrevPage.FirstRow, PrevPage.LastRow); // На случай, если предыдущая страница тоже пустая
		var NewPage      = new CTablePage(PrevPage.X, PrevPage.Y, PrevPage.XLimit, PrevPage.YLimit, LastRow, PrevPage.MaxTopBorder);
		NewPage.FirstRow = LastRow;
		NewPage.LastRow  = LastRow - 1;

		this.Pages[PageIndex] = NewPage;
	}
};
CTable.prototype.Recalculate_Grid = function()
{
	this.private_RecalculateGrid();
};
CTable.prototype.SaveRecalculateObject = function()
{
	var RecalcObj = new CTableRecalculateObject();
	RecalcObj.Save(this);
	return RecalcObj;
};
CTable.prototype.LoadRecalculateObject = function(RecalcObj)
{
	RecalcObj.Load(this);
};
CTable.prototype.PrepareRecalculateObject = function()
{
	this.TableSumGrid  = [];
	this.TableGridCalc = [];

	this.TableRowsBottom = [];
	this.RowsInfo        = [];

	this.HeaderInfo = {
		Count     : 0,
		H         : 0,
		PageIndex : 0,
		Pages     : []
	};

	this.Pages = [];

	this.MaxTopBorder = [];
	this.MaxBotBorder = [];
	this.MaxBotMargin = [];

	this.RecalcInfo.Reset(true);

	var Count = this.Content.length;
	for (var Index = 0; Index < Count; Index++)
	{
		this.Content[Index].PrepareRecalculateObject();
	}
};
CTable.prototype.StartFromNewPage = function()
{
	this.Pages.length = 1;
	this.Pages[0]     = new CTablePage(0, 0, 0, 0, 0, 0);

	this.HeaderInfo.Pages[0]      = {};
	this.HeaderInfo.Pages[0].Draw = false;

	this.RowsInfo[0]              = {};
	this.RowsInfo[0].Pages        = 1;
	this.RowsInfo[0].Y            = [];
	this.RowsInfo[0].H            = [];
	this.RowsInfo[0].TopDy        = [];
	this.RowsInfo[0].MaxTopBorder = [];
	this.RowsInfo[0].FirstPage    = false;
	this.RowsInfo[0].StartPage    = 0;

	this.RowsInfo[0].X0           = 0;
	this.RowsInfo[0].X1           = 0;
	this.RowsInfo[0].MaxBotBorder = 0;

	this.RowsInfo[0].Y[0]            = 0.0;
	this.RowsInfo[0].H[0]            = 0.0;
	this.RowsInfo[0].TopDy[0]        = 0.0;
	this.RowsInfo[0].MaxTopBorder[0] = 0.0;

	// Обнуляем таблицу суммарных высот ячеек
	for (var Index = -1; Index < this.Content.length; Index++)
	{
		this.TableRowsBottom[Index]    = [];
		this.TableRowsBottom[Index][0] = 0;
	}

	this.Pages[0].MaxBotBorder = 0;
	this.Pages[0].BotBorders   = [];

	if (this.Content.length > 0)
	{
		var CellsCount = this.Content[0].Get_CellsCount();
		for (var CurCell = 0; CurCell < CellsCount; CurCell++)
		{
			var Cell = this.Content[0].Get_Cell(CurCell);
			Cell.Content.StartFromNewPage();
			Cell.PagesCount = 2;
		}
	}
};
//----------------------------------------------------------------------------------------------------------------------
// Приватные функции связанные с рассчетом таблицы.
//----------------------------------------------------------------------------------------------------------------------
CTable.prototype.private_RecalculateCheckPageColumnBreak = function(CurPage)
{
    if (true !== this.Is_Inline()) // Случай Flow разбирается в Document.js
        return true;

    if (!this.LogicDocument || this.Parent !== this.LogicDocument)
        return true;

    var isPageBreakOnPrevLine   = false;
    var isColumnBreakOnPrevLine = false;

    var PrevElement = this.Get_DocumentPrev();

    if (null !== PrevElement && type_Paragraph === PrevElement.Get_Type() && true === PrevElement.Is_Empty() && undefined !== PrevElement.Get_SectionPr())
        PrevElement = PrevElement.Get_DocumentPrev();

    if ((0 === CurPage || true === this.Check_EmptyPages(CurPage - 1)) && null !== PrevElement && type_Paragraph === PrevElement.Get_Type())
    {
        var bNeedPageBreak = true;
        if (undefined !== PrevElement.Get_SectionPr())
        {
            var PrevSectPr = PrevElement.Get_SectionPr();
            var CurSectPr  = this.LogicDocument.SectionsInfo.Get_SectPr(this.Index).SectPr;
            if (c_oAscSectionBreakType.Continuous !== CurSectPr.Get_Type() || true !== CurSectPr.Compare_PageSize(PrevSectPr))
                bNeedPageBreak = false;
        }

        if (true === bNeedPageBreak)
        {
            var EndLine = PrevElement.Pages[PrevElement.Pages.length - 1].EndLine;
            if (-1 !== EndLine && PrevElement.Lines[EndLine].Info & paralineinfo_BreakRealPage)
                isPageBreakOnPrevLine = true;
        }
    }

    // ColumnBreak для случая CurPage > 0 не разбираем здесь, т.к. он срабатывает автоматически
    if (0 === CurPage && null !== PrevElement && type_Paragraph === PrevElement.Get_Type())
    {
        var EndLine = PrevElement.Pages[PrevElement.Pages.length - 1].EndLine;
        if (-1 !== EndLine && !(PrevElement.Lines[EndLine].Info & paralineinfo_BreakRealPage) && PrevElement.Lines[EndLine].Info & paralineinfo_BreakPage)
            isColumnBreakOnPrevLine = true;
    }

    if ((true === isPageBreakOnPrevLine && (0 !== this.private_GetColumnIndex(CurPage) || (0 === CurPage && null !== PrevElement)))
        || (true === isColumnBreakOnPrevLine && 0 === CurPage))
    {
        this.private_RecalculateSkipPage(CurPage);
        return false;
    }

    return true;
};
CTable.prototype.private_RecalculateGrid = function()
{
    //if ( true != this.RecalcInfo.TableGrid )
    //    return;

    if ( this.Content.length <= 0 )
        return;

    //---------------------------------------------------------------------------
    // 1 часть пересчета ширины таблицы : Рассчитываем фиксированную ширину
    //---------------------------------------------------------------------------
    var TablePr = this.Get_CompiledPr(false).TablePr;

    var Grid    = this.TableGrid;
    var SumGrid = [];

    var TempSum = 0;
    SumGrid[-1] = 0;
    for ( var Index = 0; Index < Grid.length; Index++ )
    {
        TempSum += Grid[Index];
        SumGrid[Index] = TempSum;
    }

    var PctWidth = this.private_RecalculatePercentWidth();
    var MinWidth = this.Internal_Get_TableMinWidth();
    var TableW = 0;
    if (tblwidth_Auto === TablePr.TableW.Type)
    {
        TableW = 0;
    }
    else if (tblwidth_Nil === TablePr.TableW.Type)
    {
        TableW = MinWidth;
    }
    else
    {
        if (tblwidth_Pct === TablePr.TableW.Type)
        {
            TableW = PctWidth * TablePr.TableW.W / 100;
        }
        else
        {
            TableW = TablePr.TableW.W
        }

        if (TableW < MinWidth)
            TableW = MinWidth;
    }

	var CurGridCol = 0;
	for (var Index = 0; Index < this.Content.length; Index++)
	{
		var Row = this.Content[Index];
		Row.Set_Index(Index);

		// Смотрим на ширину пропущенных колонок сетки в начале строки
		var BeforeInfo = Row.Get_Before();
		CurGridCol     = BeforeInfo.GridBefore;
		if (CurGridCol > 0 && SumGrid[CurGridCol - 1] < BeforeInfo.WBefore.W)
		{
			var nTempDiff = BeforeInfo.WBefore.W - SumGrid[CurGridCol - 1];
			for (var nTempIndex = CurGridCol - 1; nTempIndex < SumGrid.length; ++nTempIndex)
				SumGrid[nTempIndex] += nTempDiff;
		}

		var CellsCount = Row.Get_CellsCount();
		for (var CellIndex = 0; CellIndex < CellsCount; CellIndex++)
		{
			var Cell = Row.Get_Cell(CellIndex);
			Cell.Set_Index(CellIndex);
			var CellW    = Cell.Get_W();
			var GridSpan = Cell.Get_GridSpan();

			if (CurGridCol + GridSpan - 1 > SumGrid.length)
			{
				for (var AddIndex = SumGrid.length; AddIndex <= CurGridCol + GridSpan - 1; AddIndex++)
					SumGrid[AddIndex] = SumGrid[AddIndex - 1] + 20; // Добавляем столбик шириной в 2 см
			}

			if (tblwidth_Auto !== CellW.Type && tblwidth_Nil !== CellW.Type)
			{
				var CellWidth = 0;
				if (tblwidth_Pct === CellW.Type)
					CellWidth = PctWidth * CellW.W / 100;
				else
					CellWidth = CellW.W;

				if (CellWidth + SumGrid[CurGridCol - 1] > SumGrid[CurGridCol + GridSpan - 1])
				{
					var nTempDiff = CellWidth + SumGrid[CurGridCol - 1] - SumGrid[CurGridCol + GridSpan - 1];
					for (var nTempIndex = CurGridCol + GridSpan - 1; nTempIndex < SumGrid.length; ++nTempIndex)
						SumGrid[nTempIndex] += nTempDiff;
				}
			}

			CurGridCol += GridSpan;
		}

		// Смотрим на ширину пропущенных колонок сетки в конце строки
		var AfterInfo = Row.Get_After();
		if (CurGridCol + AfterInfo.GridAfter - 1 > SumGrid.length)
		{
			for (var AddIndex = SumGrid.length; AddIndex <= CurGridCol + AfterInfo.GridAfter - 1; AddIndex++)
				SumGrid[AddIndex] = SumGrid[AddIndex - 1] + 20; // Добавляем столбик шириной в 2 см
		}

		if (SumGrid[CurGridCol + AfterInfo.GridAfter - 1] < AfterInfo.WAfter + SumGrid[CurGridCol - 1])
		{
			var nTempDiff = AfterInfo.WAfter + SumGrid[CurGridCol - 1] - SumGrid[CurGridCol + AfterInfo.GridAfter - 1];
			for (var nTempIndex = CurGridCol + AfterInfo.GridAfter - 1; nTempIndex < SumGrid.length; ++nTempIndex)
				SumGrid[nTempIndex] += nTempDiff;
		}
	}

    // TODO: разобраться с минимальной шириной таблицы и ячеек

    // Задана общая ширина таблицы и последняя ячейка вышла за пределы
    // данной ширины. Уменьшаем все столбцы сетки пропорционально, чтобы
    // суммарная ширина стала равной заданной ширине таблицы.
    if ( TableW > 0 && Math.abs( SumGrid[SumGrid.length - 1] - TableW ) > 0.01 )
    {
        SumGrid = this.Internal_ScaleTableWidth( SumGrid, TableW );
    }
    else if ( MinWidth > SumGrid[SumGrid.length - 1] )
        SumGrid = this.Internal_ScaleTableWidth( SumGrid, SumGrid[SumGrid.length - 1] );

    // По массиву SumGrid восстанавливаем ширины самих колонок
    this.TableGridCalc = [];
	this.TableGridCalc[0] = SumGrid[0];
    for ( var Index = 1; Index < SumGrid.length; Index++ )
		this.TableGridCalc[Index] = SumGrid[Index] - SumGrid[Index - 1];

    this.TableSumGrid = SumGrid;

    var TopTable = this.Parent.Is_InTable(true);
    if ( ( null === TopTable && tbllayout_AutoFit === TablePr.TableLayout) || ( null != TopTable && tbllayout_AutoFit === TopTable.Get_CompiledPr(false).TablePr.TableLayout ) )
    {
        //---------------------------------------------------------------------------
        // 2 часть пересчета ширины таблицы : Рассчитываем ширину по содержимому
        //---------------------------------------------------------------------------
        var MinMargin = [], MinContent = [], MaxContent = [], MaxFlags = [];

        var GridCount = this.TableGridCalc.length;
        for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
        {
            MinMargin[CurCol]  = 0;
            MinContent[CurCol] = 0;
            MaxContent[CurCol] = 0;
            MaxFlags[CurCol]   = false; // false - ориентируемся на содержимое ячеек, true - ориентируемся только на ширину ячеек записанную в свойствах
        }

        // 1. Рассчитаем MinContent и MinMargin для всех колонок таблицы, причем, если
        //    у ячейки GridSpan > 1, тогда MinMargin учитывается только в первую колнонку,
        //    а MinContent распределяется равномерно по всем колонкам.

        var LeftMargin = 0, RightMargin = 0;
        var RowsCount = this.Content.length;
        for ( var CurRow = 0; CurRow < RowsCount; CurRow++ )
        {
            var Row = this.Content[CurRow];

            var Spacing  = Row.Get_CellSpacing();
            var SpacingW = ( null != Spacing ? Spacing : 0 );

            var CurGridCol = 0;

            // Смотрим на ширину пропущенных колонок сетки в начале строки
            var BeforeInfo = Row.Get_Before();
            var GridBefore = BeforeInfo.GridBefore;
            var WBefore    = BeforeInfo.WBefore;

            var WBeforeW   = null;
            if (tblwidth_Mm === WBefore.Type)
                WBeforeW = WBefore.W;
            else if (tblwidth_Pct === WBefore.Type)
                WBeforeW = PctWidth * WBefore.W / 100;

            if ( 1 === GridBefore )
            {
                if (null !== WBeforeW)
                {
                    if (MinContent[CurGridCol] < WBeforeW)
                        MinContent[CurGridCol] = WBeforeW;

                    if (false === MaxFlags[CurGridCol])
                    {
                        MaxFlags[CurGridCol] = true;
                        MaxContent[CurGridCol] = WBeforeW;
                    }
                    else if (MaxContent[CurGridCol] < WBeforeW)
                        MaxContent[CurGridCol] = WBeforeW;
                }
            }
            else if ( GridBefore > 1 )
            {
                var SumSpanMinContent = 0;
                var SumSpanMaxContent = 0;
                var SumSpanCurContent = 0;
                for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridBefore; CurSpan++ )
                {
                    SumSpanMinContent += MinContent[CurSpan];
                    SumSpanMaxContent += MaxContent[CurSpan];
                    SumSpanCurContent += this.TableGridCalc[CurSpan];
                }

                if (null !== WBeforeW && SumSpanMinContent < WBeforeW)
                {
                    for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridSpan; CurSpan++ )
                        MinContent[CurSpan] = WBeforeW * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                }

                // Если у нас в объединении несколько колонок, тогда явно записанная ширина ячейки не
                // перекрывает ширину ни одной из колонок, она всего лишь учавствует в определении
                // максимальной ширины.
                if (null !== WBeforeW && WBeforeW > SumSpanMaxContent)
                {
                    // TODO: На самом деле, распределение здесь идет в каком-то отношении.
                    //       Неплохо было бы выяснить как именно.
                    for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridBefore; CurSpan++ )
                        MaxContent[CurSpan] = WBeforeW * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                }
            }


            CurGridCol = BeforeInfo.GridBefore;

            var CellsCount = Row.Get_CellsCount();
            for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
            {
                var Cell         = Row.Get_Cell( CurCell );
                var CellMinMax   = Cell.Content_RecalculateMinMaxContentWidth(false);
                var CellMin      = CellMinMax.Min;
                var CellMax      = CellMinMax.Max;
                var GridSpan     = Cell.Get_GridSpan();
                var CellMargins  = Cell.Get_Margins();
                var CellW        = Cell.Get_W();
                var CellRBorder  = Cell.Get_Border(1);
                var CellLBorder  = Cell.Get_Border(3);
                var CellWW       = null;

                var Add = ( ( 0 === CurCell || CellsCount - 1 === CurCell ) ? 3 / 2 * SpacingW : SpacingW );

                CellMin += Add;
                CellMax += Add;

                if (tblwidth_Mm === CellW.Type)
                    CellWW = CellW.W + Add;
                else if (tblwidth_Pct === CellW.Type)
                    CellWW = PctWidth * CellW.W / 100 + Add;

                // Если GridSpan > 1, тогда все равно маргины учитываются в первую колоноку спана

                var CellMarginsW = 0;
                if ( null !== Spacing )
                {
                    CellMarginsW = CellMargins.Left.W + CellMargins.Right.W;

                    if ( border_None !== CellRBorder.Value )
                        CellMarginsW += CellRBorder.Size;

                    if ( border_None !== CellLBorder.Value )
                        CellMarginsW += CellLBorder.Size;
                }
                else
                {
                    if ( border_None !== CellRBorder.Value )
                        CellMarginsW += Math.max( CellRBorder.Size / 2, CellMargins.Right.W );
                    else
                        CellMarginsW += CellMargins.Right.W;

                    if ( border_None !== CellLBorder.Value )
                        CellMarginsW += Math.max( CellLBorder.Size / 2, CellMargins.Left.W );
                    else
                        CellMarginsW += CellMargins.Left.W;
                }

                if ( MinMargin[CurGridCol] < CellMarginsW )
                    MinMargin[CurGridCol] = CellMarginsW;

                // На самом деле, случай 1 === GridSpan нормально обработается и как случай GridSpan > 1,
                // но поскольку он наиболее распространен, делаем его обработку максимально быстрой (без циклов)
                if ( 1 === GridSpan )
                {
                    if ( MinContent[CurGridCol] < CellMin )
                        MinContent[CurGridCol] = CellMin;

                    if ( false === MaxFlags[CurGridCol] && MaxContent[CurGridCol] < CellMax )
                        MaxContent[CurGridCol] = CellMax;

                    if (null !== CellWW)
                    {
                        if (false === MaxFlags[CurGridCol])
                        {
                            MaxFlags[CurGridCol]   = true;
                            MaxContent[CurGridCol] = CellWW;
                        }
                        else if (MaxContent[CurGridCol] < CellWW)
                        {
                            MaxContent[CurGridCol] = CellWW;
                        }
                    }
                }
                else
                {
                    var SumSpanMinContent = 0;
                    var SumSpanMaxContent = 0;
                    var SumSpanCurContent = 0;
                    for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridSpan; CurSpan++ )
                    {
                        SumSpanMinContent += MinContent[CurSpan];
                        SumSpanMaxContent += MaxContent[CurSpan];
                        SumSpanCurContent += this.TableGridCalc[CurSpan];
                    }

                    if ( SumSpanMinContent < CellMin )
                    {
                        for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridSpan; CurSpan++ )
                            MinContent[CurSpan] = CellMin * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                    }

                    // Если у нас в объединении несколько колонок, тогда явно записанная ширина ячейки не
                    // перекрывает ширину ни одной из колонок, она всего лишь учавствует в определении
                    // максимальной ширины.
                    if (null !== CellWW && CellWW > CellMax)
                        CellMax = CellWW;

                    if ( SumSpanMaxContent < CellMax )
                    {
                        // TODO: На самом деле, распределение здесь идет в каком-то отношении.
                        //       Неплохо было бы выяснить как именно.
                        for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridSpan; CurSpan++ )
                            MaxContent[CurSpan] = CellMax * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                    }
                }

                if ( 0 === CurRow && 0 === CurCell )
                    LeftMargin = CellMargins.Left.W;

                if ( 0 === CurRow && CellsCount - 1 === CurCell )
                    RightMargin = CellMargins.Right.W;

                CurGridCol += GridSpan;
            }

            var AfterInfo = Row.Get_After();
            var GridAfter = AfterInfo.GridAfter;
            var WAfter    = AfterInfo.WAfter;

            var WAfterW   = null;
            if (tblwidth_Mm === WAfter.Type)
                WAfterW = WAfter.W;
            else if (tblwidth_Pct === WAfter.Type)
                WAfterW = PctWidth * WAfter.W / 100;

            if ( 1 === GridAfter )
            {
                if (null !== WAfterW)
                {
                    if (MinContent[CurGridCol] < WAfterW)
                        MinContent[CurGridCol] = WAfterW;

                    if ( false === MaxFlags[CurGridCol] )
                    {
                        MaxFlags[CurGridCol] = true;
                        MaxContent[CurGridCol] = WAfterW;
                    }
                    else if (MaxContent[CurGridCol] < WAfterW)
                    {
                        MaxContent[CurGridCol] = WAfterW;
                    }
                }
            }
            else if ( GridAfter > 1 )
            {
                var SumSpanMinContent = 0;
                var SumSpanMaxContent = 0;
                var SumSpanCurContent = 0;
                for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridAfter; CurSpan++ )
                {
                    SumSpanMinContent += MinContent[CurSpan];
                    SumSpanMaxContent += MaxContent[CurSpan];
                    SumSpanCurContent += this.TableGridCalc[CurSpan];
                }

                if (null !== WAfterW && SumSpanMinContent < WAfterW)
                {
                    for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridSpan; CurSpan++ )
                        MinContent[CurSpan] = WAfterW * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                }

                // Если у нас в объединении несколько колонок, тогда явно записанная ширина ячейки не
                // перекрывает ширину ни одной из колонок, она всего лишь учавствует в определении
                // максимальной ширины.
                if (null !== WAfterW && WAfterW > SumSpanMaxContent )
                {
                    // TODO: На самом деле, распределение здесь идет в каком-то отношении.
                    //       Неплохо было бы выяснить как именно.
                    for ( var CurSpan = CurGridCol; CurSpan < CurGridCol + GridAfter; CurSpan++ )
                        MaxContent[CurSpan] = WAfterW * this.TableGridCalc[CurSpan] / SumSpanCurContent;
                }
            }
        }

        for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
        {
            if ( true === MaxFlags[CurCol] )
                MaxContent[CurCol] = Math.max( 0, MaxContent[CurCol] - MinMargin[CurCol] );

            if (MaxContent[CurCol] < MinContent[CurCol])
                MaxContent[CurCol] = MinContent[CurCol];
        }

        // 2. Проследим, чтобы значения MinContent + MinMargin и MaxContent + MinMargin не превосходили
        //    значение 55,87см(так работает Word)
        for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
        {
            if ( MinMargin[CurCol] + MinContent[CurCol] > 558.7 )
                MinContent[CurCol] = Math.max(558.7 - MinMargin[CurCol] , 0);

            if ( MinMargin[CurCol] + MaxContent[CurCol] > 558.7 )
                MaxContent[CurCol] = Math.max(558.7 - MinMargin[CurCol] , 0);
        }

        // 3. Рассчитаем максимально допустимую ширину под всю таблицу
        var PageFields = this.Parent.Get_ColumnFields ? this.Parent.Get_ColumnFields(this.Get_Index(), this.Get_AbsoluteColumn(this.PageNum)) : this.Parent.Get_PageFields(this.private_GetRelativePageIndex(this.PageNum));
        var MaxTableW = PageFields.XLimit - PageFields.X - TablePr.TableInd;
        if ( null === TopTable )
            MaxTableW += LeftMargin + RightMargin; // Добавляем левый маргин первой ячейки + правый маргин правой ячейки для верхних таблиц

        var TableSpacing = this.Content[0].Get_CellSpacing();
        if ( null != TableSpacing )
            MaxTableW += 2 * TableSpacing;

        // 4. Рассчитаем желаемую ширину таблицы таблицы
        // Цифра 2 означает добавочная разница
        var MaxContent2 = [];
        var SumMin = 0, SumMinMargin = 0, SumMinContent = 0, SumMax = 0, SumMaxContent2 = 0;
        var TableGrid2 = [];
        for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
        {
            var Temp = MinMargin[CurCol] + MinContent[CurCol];
            TableGrid2[CurCol] = this.TableGridCalc[CurCol];
            if ( Temp < this.TableGridCalc[CurCol] )
            {
                TableGrid2[CurCol] = this.TableGridCalc[CurCol];
            }
            else
            {
                TableGrid2[CurCol] = Temp;
            }

            MaxContent2[CurCol] = Math.max( 0, MaxContent[CurCol] - MinContent[CurCol] );

            SumMin         += Temp;
            SumMaxContent2 += MaxContent2[CurCol];
            SumMinMargin   += MinMargin[CurCol];
            SumMinContent  += MinContent[CurCol];
            SumMax         += MinMargin[CurCol] + MinContent[CurCol] + MaxContent2[CurCol];
        }

        if ((tblwidth_Mm === TablePr.TableW.Type || tblwidth_Pct === TablePr.TableW.Type) && MaxTableW < TableW)
            MaxTableW = TableW;

        if ( SumMin < MaxTableW )
        {
            // SumMin < MaxTableW, значит у нас есть свободное пространство для распределения
            // У нас есть три типа ширины: Min < Preffered < Max

            var SumMin = 0, SumPreffered = 0, SumMax = 0;
            var PreffOverMin = [], MaxOverPreff = [];
            var SumPreffOverMin = 0, SumMaxOverPreff = 0;
            var PreffContent = [];

            for (var CurCol = 0; CurCol < GridCount; ++CurCol)
            {
                var MinW   = MinMargin[CurCol] + MinContent[CurCol];
                var MaxW   = MinMargin[CurCol] + MaxContent[CurCol];
                var PreffW = (true === MaxFlags[CurCol] ? MaxW : MinW);

                SumMin       += MinW;
                SumPreffered += PreffW;
                SumMax       += MaxW;

                PreffContent[CurCol] = PreffW - MinMargin[CurCol];
                PreffOverMin[CurCol] = Math.max(0, PreffW - MinW);
                MaxOverPreff[CurCol] = Math.max(0, MaxW - PreffW);

                SumPreffOverMin += PreffOverMin[CurCol];
                SumMaxOverPreff += MaxOverPreff[CurCol];
            }

            if ( SumMax <= MaxTableW || SumMaxContent2 < 0.001 )
            {
                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    this.TableGridCalc[CurCol] = MinMargin[CurCol] + Math.max(MinContent[CurCol], MaxContent[CurCol]);
                }
            }
            else
            {
                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    this.TableGridCalc[CurCol] = MinMargin[CurCol] + MinContent[CurCol] + (MaxTableW - SumMin) * MaxContent2[CurCol] / SumMaxContent2;
                }
            }

            // Если у таблицы задана ширина, тогда ориентируемся по ширине, а если нет, тогда ориентируемся по
            // максимальным значениям.
            if (tblwidth_Mm === TablePr.TableW.Type || tblwidth_Pct === TablePr.TableW.Type)
            {
                if (SumMin < 0.001 && SumMax < 0.001)
                {
                    // Распределяем ширину по количеству колонок
                    for (var CurCol = 0; CurCol < GridCount; ++CurCol)
                    {
                        this.TableGridCalc[CurCol] = TableW / GridCount;
                    }
                }
                else if (SumMin >= TableW)
                {
                    // Выставляем минимальные значения
                    for (var CurCol = 0; CurCol < GridCount; ++CurCol)
                    {
                        this.TableGridCalc[CurCol] = MinMargin[CurCol] + MinContent[CurCol];
                    }
                }
                else if (SumPreffered >= TableW && SumPreffOverMin > 0.001)
                {
                    // Растягиваем только те колонки, в которых заданы предпочитаемые ширины
                    for (var CurCol = 0; CurCol < GridCount; ++CurCol)
                    {
                        this.TableGridCalc[CurCol] = MinMargin[CurCol] + MinContent[CurCol] + (TableW - SumMin) * PreffOverMin[CurCol] / SumPreffOverMin;
                    }
                }
                else
                {
                    // Если данное условие выполняется, значит у нас все ячейки с предпочитаемыми значениями, тогда
                    // мы растягиваем все ячейки равномерно. Если не выполняется, значит есть ячейки, в которых
                    // предпочитаемое значение не задано, и тогда растягиваем только такие ячейки.
                    if (Math.abs(SumMax - SumPreffered) < 0.001)
                    {
                        if (SumMax >= TableW)
                        {
                            for (var CurCol = 0; CurCol < GridCount; ++CurCol)
                            {
                                this.TableGridCalc[CurCol] = MinMargin[CurCol] + MinContent[CurCol] + (TableW - SumMin) * MaxContent2[CurCol] / SumMaxContent2;
                            }
                        }
                        else
                        {
                            for (var CurCol = 0; CurCol < GridCount; CurCol++)
                            {
                                this.TableGridCalc[CurCol] = MinMargin[CurCol] + MaxContent[CurCol] + (TableW - SumMax) * (MinMargin[CurCol] + MaxContent[CurCol]) / SumMax;
                            }
                        }
                    }
                    else
                    {
                        for (var CurCol = 0; CurCol < GridCount; ++CurCol)
                        {
                            this.TableGridCalc[CurCol] = MinMargin[CurCol] + PreffContent[CurCol] + (TableW - SumPreffered) * MaxOverPreff[CurCol] / SumMaxOverPreff;
                        }
                    }
                }
            }
        }
        else
        {
            // 5. Если в таблице сделать все ячейки нулевой ширины (для контента), и все равно она получается шире
            //    максимальной допустимой ширины, тогда выставляем ширины всех колоно по минимальному значению
            //    маргинов и оставляем так как есть
            if (MaxTableW - SumMinMargin < 0.001)
            {
                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    this.TableGridCalc[CurCol] = MinMargin[CurCol];
                }
            }
            else
            {
                // 6. Равномерно уменьшаем все колонки до достижения суммарного значения MaxTableW
                var ColsDiff = [];
                var SumColsDiff = 0;
                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    var Temp = TableGrid2[CurCol] - MinMargin[CurCol];
                    ColsDiff[CurCol] = Temp;
                    SumColsDiff += Temp;
                }

                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    TableGrid2[CurCol] = MinMargin[CurCol] + (MaxTableW - SumMinMargin) * ColsDiff[CurCol] / SumColsDiff;
                }

                // 7. Ищем колонки, у которых текущая ширина меньше MinContent (заодно ищем недостоющую сумму).
                //    Также запоминаем остальные колонки и находим у них избыточную сумму.
                var SumN = 0, SumI = 0;
                var GridCols = [];
                for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                {
                    var Temp = TableGrid2[CurCol] - (MinMargin[CurCol] + MinContent[CurCol]);
                    if ( Temp >= 0 )
                    {
                        GridCols[CurCol] = Temp;
                        SumI += Temp;
                    }
                    else
                    {
                        GridCols[CurCol] = -1;
                        SumN -= Temp;
                    }
                }

                // 8. Если недостающего пространста больше, чем избыточного, тогда ищем разницу
                //    (MaxTableW - SumMinMargin) и распределяем ее в отношении, как соотносятся
                //    значения MinContent между собой.
                if (SumN > SumI || SumI < 0.001)
                {
                    if (SumMinContent > 0.001)
                    {
                        var SumDiff = MaxTableW - SumMinMargin;
                        for (var CurCol = 0; CurCol < GridCount; CurCol++)
                        {
                            this.TableGridCalc[CurCol] = MinMargin[CurCol] + SumDiff * MinContent[CurCol] / SumMinContent;
                        }
                    }
                    else
                    {
                        for (var CurCol = 0; CurCol < GridCount; CurCol++)
                        {
                            this.TableGridCalc[CurCol] = MinMargin[CurCol];
                        }
                    }
                }
                else
                {
                    for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
                    {
                        if ( GridCols[CurCol] < 0 )
                        {
                            this.TableGridCalc[CurCol] = MinMargin[CurCol] + MinContent[CurCol];
                        }
                        else
                        {
                            this.TableGridCalc[CurCol] = TableGrid2[CurCol] - SumN * GridCols[CurCol] / SumI;
                        }
                    }
                }
            }
        }


        this.TableSumGrid[-1] = 0;
        for ( var CurCol = 0; CurCol < GridCount; CurCol++ )
            this.TableSumGrid[CurCol] = this.TableSumGrid[CurCol - 1] + this.TableGridCalc[CurCol];
    }

    this.RecalcInfo.TableGrid = false;
};
CTable.prototype.private_RecalculateBorders = function()
{
    if ( true != this.RecalcInfo.TableBorders )
        return;

    // Обнуляем таблицу суммарных высот ячеек
    for ( var Index = -1; Index < this.Content.length; Index++ )
    {
        this.TableRowsBottom[Index] = [];
        this.TableRowsBottom[Index][0] = 0;
    }

    // Изначально найдем верхние границы и (если нужно) нижние границы
    // для каждой ячейки.
    var MaxTopBorder = [];
    var MaxBotBorder = [];
    var MaxBotMargin = [];

    for ( var Index = 0; Index < this.Content.length; Index++ )
    {
        MaxBotBorder[Index] = 0;
        MaxTopBorder[Index] = 0;
        MaxBotMargin[Index] = 0;
    }

    var TablePr = this.Get_CompiledPr(false).TablePr;
    var TableBorders = this.Get_Borders();

    for ( var CurRow = 0; CurRow < this.Content.length; CurRow++ )
    {
        var Row         = this.Content[CurRow];
        var CellsCount  = Row.Get_CellsCount();
        var CellSpacing = Row.Get_CellSpacing();

        var BeforeInfo = Row.Get_Before();
        var AfterInfo  = Row.Get_After();
        var CurGridCol = BeforeInfo.GridBefore;

        // Нам нужно пробежаться по текущей строке и выяснить максимальное значение ширины верхней
        // границы и ширины нижней границы, заодно рассчитаем вид границы у каждой ячейки,
        // также надо рассчитать максимальное значение нижнего отступа всей строки.

        var bSpacing_Top = false;
        var bSpacing_Bot = false;

        if ( null != CellSpacing )
        {
            bSpacing_Bot = true;
            bSpacing_Top = true;
        }
        else
        {
            if ( 0 != CurRow )
            {
                var PrevCellSpacing = this.Content[CurRow - 1].Get_CellSpacing();
                if ( null != PrevCellSpacing )
                    bSpacing_Top = true;
            }

            if ( this.Content.length - 1 != CurRow )
            {
                var NextCellSpacing = this.Content[CurRow + 1].Get_CellSpacing();
                if ( null != NextCellSpacing )
                    bSpacing_Bot = true;
            }
        }

        Row.Set_SpacingInfo( bSpacing_Top, bSpacing_Bot );

        for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
        {
            var Cell     = Row.Get_Cell( CurCell );
            var GridSpan = Cell.Get_GridSpan();
            var Vmerge   = Cell.Get_VMerge();

            Row.Set_CellInfo( CurCell, CurGridCol, 0, 0, 0, 0, 0, 0 );

            // Bug 32418 ячейки, участвующие в вертикальном объединении, все равно участвуют в определении границы
            // строки, поэтому здесь мы их не пропускаем.

            var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan );

            var CellMargins = Cell.Get_Margins();
            if ( CellMargins.Bottom.W > MaxBotMargin[CurRow + VMergeCount - 1] )
                MaxBotMargin[CurRow + VMergeCount - 1] = CellMargins.Bottom.W;

            var CellBorders = Cell.Get_Borders();
            if ( true === bSpacing_Top )
            {
                if ( border_Single === CellBorders.Top.Value && MaxTopBorder[CurRow] < CellBorders.Top.Size )
                    MaxTopBorder[CurRow] = CellBorders.Top.Size;

                Cell.Set_BorderInfo_Top( [ CellBorders.Top ] );
            }
            else
            {
                if ( 0 === CurRow )
                {
                    // Сравним границы
                    var Result_Border = this.Internal_CompareBorders( TableBorders.Top, CellBorders.Top, true, false );
                    if ( border_Single === Result_Border.Value && MaxTopBorder[CurRow] < Result_Border.Size )
                        MaxTopBorder[CurRow] = Result_Border.Size;

                    var BorderInfo_Top = [];
                    for ( var TempIndex = 0; TempIndex < GridSpan; TempIndex++ )
                        BorderInfo_Top.push( Result_Border );

                    Cell.Set_BorderInfo_Top( BorderInfo_Top );
                }
                else
                {
                    // Ищем в предыдущей строке первую ячейку, пересекающуюся с [CurGridCol, CurGridCol + GridSpan]
                    var Prev_Row = this.Content[CurRow - 1];
                    var Prev_CellsCount = Prev_Row.Get_CellsCount();
                    var Prev_BeforeInfo = Prev_Row.Get_Before();
                    var Prev_AfterInfo  = Prev_Row.Get_After();

                    var Prev_Pos = -1;

                    var Prev_GridCol = Prev_BeforeInfo.GridBefore;
                    for ( var PrevCell = 0; PrevCell < Prev_CellsCount; PrevCell++ )
                    {
                        var Prev_Cell      = Prev_Row.Get_Cell( PrevCell );
                        var Prev_GridSpan  = Prev_Cell.Get_GridSpan();

                        if ( Prev_GridCol <= CurGridCol + GridSpan - 1 && Prev_GridCol + Prev_GridSpan - 1 >= CurGridCol )
                        {
                            Prev_Pos = PrevCell;
                            break;
                        }

                        Prev_GridCol += Prev_GridSpan;
                    }

                    var Border_Top_Info = [];

                    // Сначала посмотрим пересечение с GridBefore предыдущей строки
                    if ( CurGridCol <= Prev_BeforeInfo.GridBefore - 1 )
                    {
                        var Result_Border = this.Internal_CompareBorders( TableBorders.Left, CellBorders.Top, true, false );
                        if ( border_Single === Result_Border.Value && MaxTopBorder[CurRow] < Result_Border.Size )
                            MaxTopBorder[CurRow] = Result_Border.Size;

                        var AddCount = Math.min( Prev_BeforeInfo.GridBefore - CurGridCol, GridSpan );
                        for ( var TempIndex = 0; TempIndex < AddCount; TempIndex++ )
                            Border_Top_Info.push( Result_Border );
                    }

                    if ( -1 != Prev_Pos )
                    {
                        while ( Prev_GridCol <= CurGridCol + GridSpan - 1 && Prev_Pos < Prev_CellsCount )
                        {
                            var Prev_Cell      = Prev_Row.Get_Cell( Prev_Pos );
                            var Prev_GridSpan  = Prev_Cell.Get_GridSpan();

                            // Если данная ячейка учавствует в вертикальном объединении,
                            // тогда найдем нижнюю ячейку.

                            var Prev_VMerge = Prev_Cell.Get_VMerge();
                            if ( vmerge_Continue === Prev_VMerge )
                                Prev_Cell = this.Internal_Get_EndMergedCell(CurRow - 1, Prev_GridCol, Prev_GridSpan);

                            var PrevBorders = Prev_Cell.Get_Borders();

                            // Сравним границы
                            var Result_Border = this.Internal_CompareBorders( PrevBorders.Bottom, CellBorders.Top, false, false );
                            if ( border_Single === Result_Border.Value && MaxTopBorder[CurRow] < Result_Border.Size )
                                MaxTopBorder[CurRow] = Result_Border.Size;

                            // Надо добавить столько раз, сколько колонок находится в пересечении этих двух ячееки
                            var AddCount = 0;
                            if ( Prev_GridCol >= CurGridCol )
                            {
                                if ( Prev_GridCol + Prev_GridSpan - 1 > CurGridCol + GridSpan - 1 )
                                    AddCount = CurGridCol + GridSpan - Prev_GridCol;
                                else
                                    AddCount = Prev_GridSpan;
                            }
                            else if ( Prev_GridCol + Prev_GridSpan - 1 > CurGridCol + GridSpan - 1 )
                                AddCount = GridSpan;
                            else
                                AddCount = Prev_GridCol + Prev_GridSpan - CurGridCol;

                            for ( var TempIndex = 0; TempIndex < AddCount; TempIndex++ )
                                Border_Top_Info.push( Result_Border );

                            Prev_Pos++;
                            Prev_GridCol += Prev_GridSpan;
                        }
                    }

                    // Посмотрим пересечение с GridAfter предыдущей строки
                    if ( Prev_AfterInfo.GridAfter > 0 )
                    {
                        var StartAfterGrid = Prev_Row.Get_CellInfo( Prev_CellsCount - 1 ).StartGridCol + Prev_Row.Get_Cell( Prev_CellsCount - 1 ).Get_GridSpan();

                        if ( CurGridCol + GridSpan - 1 >= StartAfterGrid )
                        {
                            var Result_Border = this.Internal_CompareBorders( TableBorders.Right, CellBorders.Top, true, false );
                            if ( border_Single === Result_Border.Value && MaxTopBorder[CurRow] < Result_Border.Size )
                                MaxTopBorder[CurRow] = Result_Border.Size;

                            var AddCount = Math.min( CurGridCol + GridSpan - StartAfterGrid, GridSpan );
                            for ( var TempIndex = 0; TempIndex < AddCount; TempIndex++ )
                                Border_Top_Info.push( Result_Border );
                        }
                    }

                    Cell.Set_BorderInfo_Top( Border_Top_Info );
                }
            }

            var CellBordersBottom = CellBorders.Bottom;
            if (VMergeCount > 1)
            {
                // Берем нижнюю границу нижней ячейки вертикального объединения.
                var BottomCell = this.Internal_Get_EndMergedCell(CurRow, CurGridCol, GridSpan);
                if (null !== BottomCell)
                    CellBordersBottom = BottomCell.Get_Borders().Bottom;
            }

            if ( true === bSpacing_Bot )
            {
                Cell.Set_BorderInfo_Bottom( [CellBordersBottom], -1, -1 );

                if ( border_Single === CellBordersBottom.Value && CellBordersBottom.Size > MaxBotBorder[CurRow + VMergeCount - 1] )
                    MaxBotBorder[CurRow + VMergeCount - 1] = CellBordersBottom.Size;
            }
            else
            {
                if ( this.Content.length - 1 === CurRow + VMergeCount - 1 )
                {
                    // Сравним границы
                    var Result_Border = this.Internal_CompareBorders( TableBorders.Bottom, CellBordersBottom, true, false );

                    if ( border_Single === Result_Border.Value && Result_Border.Size > MaxBotBorder[CurRow + VMergeCount - 1] )
                        MaxBotBorder[CurRow + VMergeCount - 1] = Result_Border.Size;

                    if ( GridSpan > 0 )
                    {
                        for ( var TempIndex = 0; TempIndex < GridSpan; TempIndex++ )
                            Cell.Set_BorderInfo_Bottom( [ Result_Border ], -1, -1 );
                    }
                    else
                        Cell.Set_BorderInfo_Bottom( [ ], -1, -1 );
                }
                else
                {
                    // Мы должны проверить нижнюю границу ячейки, на предмет того, что со следующей строкой
                    // она может пересекаться по GridBefore и/или GridAfter. Везде в таких местах мы должны
                    // нарисовать нижнюю границу. Пересечение с ячейками нам неинтересено, потому что этот
                    // случай будет учтен при обсчете следующей строки (там будет случай bSpacing_Top = false
                    // и 0 != CurRow )

                    var Next_Row = this.Content[CurRow + VMergeCount];
                    var Next_CellsCount = Next_Row.Get_CellsCount();
                    var Next_BeforeInfo = Next_Row.Get_Before();
                    var Next_AfterInfo  = Next_Row.Get_After();

                    var Border_Bottom_Info = [];

                    // Сначала посмотрим пересечение с GridBefore предыдущей строки
                    var BeforeCount = 0;
                    if ( CurGridCol <= Next_BeforeInfo.GridBefore - 1 )
                    {
                        var Result_Border = this.Internal_CompareBorders( TableBorders.Left, CellBordersBottom, true, false );
                        BeforeCount = Math.min( Next_BeforeInfo.GridBefore - CurGridCol, GridSpan );

                        for ( var TempIndex = 0; TempIndex < BeforeCount; TempIndex++ )
                            Border_Bottom_Info.push( Result_Border );
                    }

                    var Next_GridCol = Next_BeforeInfo.GridBefore;
                    for ( var NextCell = 0; NextCell < Next_CellsCount; NextCell++ )
                    {
                        var Next_Cell     = Next_Row.Get_Cell( NextCell );
                        var Next_GridSpan = Next_Cell.Get_GridSpan();
                        Next_GridCol += Next_GridSpan;
                    }

                    // Посмотрим пересечение с GridAfter предыдущей строки
                    var AfterCount = 0;
                    if ( Next_AfterInfo.GridAfter > 0 )
                    {
                        var StartAfterGrid = Next_GridCol;

                        if ( CurGridCol + GridSpan - 1 >= StartAfterGrid )
                        {
                            var Result_Border = this.Internal_CompareBorders( TableBorders.Right, CellBordersBottom, true, false );
                            AfterCount = Math.min( CurGridCol + GridSpan - StartAfterGrid, GridSpan );
                            for ( var TempIndex = 0; TempIndex < AfterCount; TempIndex++ )
                                Border_Bottom_Info.push( Result_Border );
                        }
                    }

                    Cell.Set_BorderInfo_Bottom( Border_Bottom_Info, BeforeCount, AfterCount );
                }
            }

            CurGridCol += GridSpan;
        }
    }

    this.MaxTopBorder = MaxTopBorder;
    this.MaxBotBorder = MaxBotBorder;
    this.MaxBotMargin = MaxBotMargin;

    // Также для каждой ячейки обсчитаем ее метрики и левую и правую границы
    for ( var CurRow = 0; CurRow < this.Content.length; CurRow++  )
    {
        var Row         = this.Content[CurRow];
        var CellsCount  = Row.Get_CellsCount();
        var CellSpacing = Row.Get_CellSpacing();

        var BeforeInfo  = Row.Get_Before();
        var AfterInfo   = Row.Get_After();
        var CurGridCol  = BeforeInfo.GridBefore;

        var Row_x_max = 0;
        var Row_x_min = 0;

        for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
        {
            var Cell     = Row.Get_Cell( CurCell );
            var GridSpan = Cell.Get_GridSpan();
            var Vmerge   = Cell.Get_VMerge();

            // начальная и конечная точки данного GridSpan'a
            var X_grid_start = this.TableSumGrid[CurGridCol - 1];
            var X_grid_end   = this.TableSumGrid[CurGridCol + GridSpan - 1];

            // границы самой ячейки
            var X_cell_start = X_grid_start;
            var X_cell_end   = X_grid_end;

            if ( null != CellSpacing )
            {

                if ( 0 === CurCell )
                {
                    if ( 0 === BeforeInfo.GridBefore )
                    {
                        if ( border_None === TableBorders.Left.Value || CellSpacing > TableBorders.Left.Size / 2 )
                            X_cell_start += CellSpacing;
                        else
                            X_cell_start += TableBorders.Left.Size / 2;
                    }
                    else
                    {
                        if ( border_None === TableBorders.Left.Value || CellSpacing > TableBorders.Left.Size ) // CellSpacing / 2 > TableBorders.Left.Size / 2
                            X_cell_start += CellSpacing / 2;
                        else
                            X_cell_start += TableBorders.Left.Size / 2;
                    }
                }
                else
                    X_cell_start += CellSpacing / 2;

                if ( CellsCount - 1 === CurCell )
                {
                    if ( 0 === AfterInfo.GridAfter )
                    {
                        if ( border_None === TableBorders.Right.Value || CellSpacing > TableBorders.Right.Size / 2 )
                            X_cell_end -= CellSpacing;
                        else
                            X_cell_end -= TableBorders.Right.Size / 2;
                    }
                    else
                    {
                        if ( border_None === TableBorders.Right.Value || CellSpacing > TableBorders.Right.Size ) // CellSpacing / 2 > TableBorders.Right.Size / 2
                            X_cell_end -= CellSpacing / 2;
                        else
                            X_cell_end -= TableBorders.Right.Size / 2;
                    }
                }
                else
                    X_cell_end -= CellSpacing / 2;
            }

            var CellMar = Cell.Get_Margins();

            var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan );

            // начальная и конечная точка для содержимого данной ячейки
            var X_content_start = X_cell_start;
            var X_content_end   = X_cell_end;

            // Левая и правая границы ячейки рисуются вовнутрь ячейки, если Spacing != null.
            var CellBorders = Cell.Get_Borders();
            if ( null != CellSpacing )
            {
                X_content_start += CellMar.Left.W;
                X_content_end   -= CellMar.Right.W;

                if ( border_Single === CellBorders.Left.Value )
                    X_content_start += CellBorders.Left.Size;

                if ( border_Single === CellBorders.Right.Value )
                    X_content_end -= CellBorders.Right.Size;
            }
            else
            {
                if ( vmerge_Continue === Vmerge )
                {
                    X_content_start += CellMar.Left.W;
                    X_content_end   -= CellMar.Right.W;
                }
                else
                {
                    // Линии правой и левой границы рисуются ровно по сетке
                    // (середина линии(всмысле толщины линии) совпадает с линией сетки).
                    // Мы должны найти максимальную толщину линии, учавствущую в правой/левой
                    // границах. Если данная толщина меньше соответствующего отступа, тогда
                    // она не влияет на расположение содержимого ячейки, в противном случае,
                    // максимальная толщина линии и задает отступ для содержимого.

                    // Поэтому первым шагом определим максимальную толщину правой и левой границ.

                    var Max_r_w = 0;
                    var Max_l_w = 0;
                    var Borders_Info =
                        {
                            Right     : [],
                            Left      : [],

                            Right_Max : 0,
                            Left_Max  : 0
                        };

                    for ( var Temp_CurRow = 0; Temp_CurRow < VMergeCount; Temp_CurRow++ )
                    {
                        var Temp_Row = this.Content[CurRow + Temp_CurRow];
                        var Temp_CellsCount = Temp_Row.Get_CellsCount();

                        // ищем ячейку текущего объединения
                        var Temp_CurCell = this.Internal_Get_Cell_ByStartGridCol( CurRow + Temp_CurRow, CurGridCol );
                        if ( Temp_CurCell < 0 )
                            continue;

                        // левая граница
                        if ( 0 === Temp_CurCell )
                        {
                            var LeftBorder = this.Internal_CompareBorders( TableBorders.Left, CellBorders.Left, true, false );
                            if ( border_Single === LeftBorder.Value && LeftBorder.Size > Max_l_w )
                                Max_l_w = LeftBorder.Size;

                            Borders_Info.Left.push( LeftBorder );
                        }
                        else
                        {
                            var Temp_Prev_Cell = Temp_Row.Get_Cell( Temp_CurCell - 1 );
                            var Temp_Prev_VMerge = Temp_Prev_Cell.Get_VMerge();
                            if ( 0 != Temp_CurRow && vmerge_Continue === Temp_Prev_VMerge )
                            {
                                Borders_Info.Left.push( Borders_Info.Left[Borders_Info.Left.length - 1] );
                            }
                            else
                            {
                                var Temp_Prev_Main_Cell = this.Internal_Get_StartMergedCell( CurRow + Temp_CurRow, CurGridCol - Temp_Prev_Cell.Get_GridSpan(), Temp_Prev_Cell.Get_GridSpan() );
                                var Temp_Prev_Main_Cell_Borders = Temp_Prev_Main_Cell.Get_Borders();

                                var LeftBorder = this.Internal_CompareBorders( Temp_Prev_Main_Cell_Borders.Right, CellBorders.Left, false, false );
                                if ( border_Single === LeftBorder.Value && LeftBorder.Size > Max_l_w )
                                    Max_l_w = LeftBorder.Size;

                                Borders_Info.Left.push( LeftBorder );
                            }
                        }

                        if ( Temp_CellsCount - 1 === Temp_CurCell )
                        {
                            var RightBorder = this.Internal_CompareBorders( TableBorders.Right, CellBorders.Right, true, false );
                            if ( border_Single === RightBorder.Value && RightBorder.Size > Max_r_w )
                                Max_r_w = RightBorder.Size;

                            Borders_Info.Right.push( RightBorder );
                        }
                        else
                        {
                            var Temp_Next_Cell = Temp_Row.Get_Cell( Temp_CurCell + 1 );
                            var Temp_Next_VMerge = Temp_Next_Cell.Get_VMerge();
                            if ( 0 != Temp_CurRow && vmerge_Continue === Temp_Next_VMerge )
                            {
                                Borders_Info.Right.push( Borders_Info.Right[Borders_Info.Right.length - 1] );
                            }
                            else
                            {
                                var Temp_Next_Main_Cell = this.Internal_Get_StartMergedCell( CurRow + Temp_CurRow, CurGridCol + GridSpan, Temp_Next_Cell.Get_GridSpan() );
                                var Temp_Next_Main_Cell_Borders = Temp_Next_Main_Cell.Get_Borders();

                                var RightBorder = this.Internal_CompareBorders( Temp_Next_Main_Cell_Borders.Left, CellBorders.Right, false, false );
                                if ( border_Single === RightBorder.Value && RightBorder.Size > Max_r_w )
                                    Max_r_w = RightBorder.Size;

                                Borders_Info.Right.push( RightBorder );
                            }
                        }
                    }

                    Borders_Info.Right_Max = Max_r_w;
                    Borders_Info.Left_Max  = Max_l_w;

                    if ( Max_l_w / 2 > CellMar.Left.W )
                        X_content_start += Max_l_w / 2;
                    else
                        X_content_start += CellMar.Left.W;

                    if ( Max_r_w / 2 > CellMar.Right.W )
                        X_content_end -= Max_r_w / 2;
                    else
                        X_content_end -= CellMar.Right.W;

                    Cell.Set_BorderInfo_Left ( Borders_Info.Left,  Max_l_w );
                    Cell.Set_BorderInfo_Right( Borders_Info.Right, Max_r_w );
                }
            }

            if ( 0 === CurCell )
            {
                if ( null != CellSpacing )
                {
                    Row_x_min = X_grid_start;
                    if ( border_Single === TableBorders.Left.Value )
                        Row_x_min -= TableBorders.Left.Size / 2;
                }
                else
                {
                    var BorderInfo = Cell.Get_BorderInfo();
                    Row_x_min = X_grid_start - BorderInfo.MaxLeft / 2;
                }
            }

            if ( CellsCount - 1 === CurCell )
            {
                if ( null != CellSpacing )
                {
                    Row_x_max = X_grid_end;
                    if ( border_Single === TableBorders.Right.Value )
                        Row_x_max += TableBorders.Right.Size / 2;
                }
                else
                {
                    var BorderInfo = Cell.Get_BorderInfo();
                    Row_x_max = X_grid_end + BorderInfo.MaxRight / 2;
                }
            }

            Cell.Set_Metrics( CurGridCol, X_grid_start, X_grid_end, X_cell_start, X_cell_end, X_content_start, X_content_end );

            CurGridCol += GridSpan;
        }

        Row.Set_Metrics_X( Row_x_min, Row_x_max );
    }

    this.RecalcInfo.TableBorders = false;
};
CTable.prototype.private_RecalculateHeader = function()
{
    // Если у нас таблица внутри таблицы, тогда в ней заголовочных строк не должно быть,
    // потому что так делает Word.
    if ( true === this.Parent.Is_TableCellContent() )
    {
        this.HeaderInfo.Count = 0;
        return;
    }

    // Здесь мы подготавливаем информацию для пересчета заголовка таблицы
    var Header_RowsCount = 0;
    var Rows_Count = this.Content.length;
    for ( var Index = 0; Index < Rows_Count; Index++ )
    {
        var Row = this.Content[Index];
        if ( true != Row.Is_Header() )
            break;

        Header_RowsCount++;
    }

    // Избавимся от строк, в которых есть вертикально объединенные ячейки, которые одновременно есть в заголовке
    // и не в заголовке
    for ( var CurRow = Header_RowsCount - 1; CurRow >= 0; CurRow-- )
    {
        var Row = this.Content[CurRow];
        var Cells_Count = Row.Get_CellsCount();

        var bContinue = false;
        for ( var CurCell = 0; CurCell < Cells_Count; CurCell++ )
        {
            var Cell        = Row.Get_Cell( CurCell );
            var GridSpan    = Cell.Get_GridSpan();
            var CurGridCol  = Cell.Metrics.StartGridCol;
            var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan );

            // В данной строке нашли вертикально объединенную ячейку с ячейкой не из заголовка
            // Поэтому выкидываем данную строку и проверяем предыдущую
            if ( VMergeCount > 1 )
            {
                Header_RowsCount--;
                bContinue = true;
                break;
            }
        }

        if ( true != bContinue )
        {
            // Если дошли до этого места, значит данная строка, а, следовательно, и все строки выше
            // нормальные в плане объединенных вертикально ячеек.
            break;
        }
    }

    this.HeaderInfo.Count = Header_RowsCount;
};
CTable.prototype.private_RecalculatePageXY = function(CurPage)
{
    var FirstRow = 0;
    if (0 !== CurPage)
    {
        if (true === this.Is_EmptyPage(CurPage - 1))
            FirstRow = this.Pages[CurPage - 1].FirstRow;
        else if (true === this.Pages[CurPage - 1].LastRowSplit)
            FirstRow = this.Pages[CurPage - 1].LastRow;
        else
            FirstRow = Math.min(this.Pages[CurPage - 1].LastRow + 1, this.Content.length - 1);
    }

    var TempMaxTopBorder = this.Get_MaxTopBorder(FirstRow);
    this.Pages.length = Math.max(CurPage, 0);
    if (0 === CurPage)
    {
        this.Pages[CurPage] = new CTablePage(this.X, this.Y, this.XLimit, this.YLimit, FirstRow, TempMaxTopBorder);
    }
    else
    {
        var StartPos = this.Parent.Get_PageContentStartPos2(this.PageNum, this.ColumnNum, CurPage, this.Index);
        this.Pages[CurPage] = new CTablePage(StartPos.X, StartPos.Y, StartPos.XLimit, StartPos.YLimit, FirstRow, TempMaxTopBorder);
    }
};
CTable.prototype.private_RecalculatePositionX = function(CurPage)
{
    var TablePr = this.Get_CompiledPr(false).TablePr;
    var PageLimits = this.Parent.Get_PageLimits(this.PageNum);
    var PageFields = this.Parent.Get_PageFields(this.PageNum);

    var LD_PageLimits = this.LogicDocument.Get_PageLimits( this.Get_StartPage_Absolute() );
    var LD_PageFields = this.LogicDocument.Get_PageFields( this.Get_StartPage_Absolute() );

    if ( true === this.Is_Inline() )
    {
        var Page = this.Pages[CurPage];
        if (0 === CurPage)
        {
            this.AnchorPosition.CalcX = this.X_origin + TablePr.TableInd;
            this.AnchorPosition.Set_X(this.TableSumGrid[this.TableSumGrid.length - 1], this.X_origin, LD_PageFields.X, LD_PageFields.XLimit, LD_PageLimits.XLimit, PageLimits.X, PageLimits.XLimit);
        }

        switch (TablePr.Jc)
        {
            case AscCommon.align_Left :
            {
                Page.X = Page.X_origin + this.Get_TableOffsetCorrection() + TablePr.TableInd;
                break;
            }
            case AscCommon.align_Right :
            {
                var TableWidth = this.TableSumGrid[this.TableSumGrid.length - 1];

                if (false === this.Parent.Is_TableCellContent())
                    Page.X = Page.XLimit - TableWidth + 1.9; // 1.9мм всегда добавляется справа от таблицы
                else
                    Page.X = Page.XLimit - TableWidth;
                break;
            }
            case AscCommon.align_Center :
            {
                var TableWidth = this.TableSumGrid[this.TableSumGrid.length - 1];
                var RangeWidth = Page.XLimit - Page.X_origin;

                Page.X = Page.X_origin + ( RangeWidth - TableWidth ) / 2;
                break;
            }
        }
    }
    else
    {
        if (0 === CurPage)
        {
            var OffsetCorrection_Left  = 0;
            var OffsetCorrection_Right = 0;

            if (this.Content.length > 0 && this.Content[0].Get_CellsCount() > 0 && !(this.bPresentation === true))
            {
                var FirstRow   = this.Content[0];
                var Cell_Left  = FirstRow.Get_Cell(0);
                var Cell_Right = FirstRow.Get_Cell(FirstRow.Get_CellsCount() - 1);

                OffsetCorrection_Left  = Cell_Left.Get_Margins().Left.W;
                OffsetCorrection_Right = Cell_Right.Get_Margins().Right.W;
            }

            this.X = this.X_origin + this.Get_TableOffsetCorrection();
            this.AnchorPosition.Set_X(this.TableSumGrid[this.TableSumGrid.length - 1], this.X_origin, PageFields.X - OffsetCorrection_Left, PageFields.XLimit + OffsetCorrection_Right, LD_PageLimits.XLimit, PageLimits.X - OffsetCorrection_Left, PageLimits.XLimit + OffsetCorrection_Right);

            this.X        = this.AnchorPosition.Calculate_X(this.PositionH.RelativeFrom, this.PositionH.Align, this.PositionH.Value);
            this.X_origin = this.X - this.Get_TableOffsetCorrection();

            if (undefined != this.PositionH_Old)
            {
                // Восстанови старые значения, чтобы в историю изменений все нормально записалось
                this.PositionH.RelativeFrom = this.PositionH_Old.RelativeFrom;
                this.PositionH.Align        = this.PositionH_Old.Align;
                this.PositionH.Value        = this.PositionH_Old.Value;

                // Рассчитаем сдвиг с учетом старой привязки
                var Value = this.AnchorPosition.Calculate_X_Value(this.PositionH_Old.RelativeFrom);
                this.Set_PositionH(this.PositionH_Old.RelativeFrom, false, Value);
                // На всякий случай пересчитаем заново координату
                this.X        = this.AnchorPosition.Calculate_X(this.PositionH.RelativeFrom, this.PositionH.Align, this.PositionH.Value);
                this.X_origin = this.X - this.Get_TableOffsetCorrection();

                this.PositionH_Old = undefined;
            }
        }

        this.Pages[CurPage].X        = this.X;
        this.Pages[CurPage].XLimit   = this.XLimit;
        this.Pages[CurPage].X_origin = this.X_origin;
    }
};
CTable.prototype.private_RecalculatePage = function(CurPage)
{
    if ( true === this.TurnOffRecalc )
        return;

	var isInnerTable       = this.Parent.Is_TableCellContent();
	var oTopDocument       = this.Parent.Is_TopDocument(true);
	var isTopLogicDocument = (oTopDocument instanceof CDocument ? true : false);
	var oFootnotes         = (isTopLogicDocument && !isInnerTable ? oTopDocument.Footnotes : null);
	var nPageAbs           = this.Get_AbsolutePage(CurPage);
	var nColumnAbs         = this.Get_AbsoluteColumn(CurPage);

    this.TurnOffRecalc = true;

    var FirstRow = 0;
    var LastRow  = 0;

    var ResetStartElement = false;
    if ( 0 === CurPage )
    {
        // Обнуляем таблицу суммарных высот ячеек
        for ( var Index = -1; Index < this.Content.length; Index++ )
        {
            this.TableRowsBottom[Index] = [];
            this.TableRowsBottom[Index][0] = 0;
        }
    }
    else
    {
        if (true === this.Is_EmptyPage(CurPage - 1))
        {
            ResetStartElement = false;
            FirstRow = this.Pages[CurPage - 1].FirstRow;
        }
        else if (true === this.Pages[CurPage - 1].LastRowSplit)
        {
            ResetStartElement = false;
            FirstRow = this.Pages[CurPage - 1].LastRow;
        }
        else
        {
            ResetStartElement = true;
            FirstRow = Math.min(this.Pages[CurPage - 1].LastRow + 1, this.Content.length - 1);
        }

        LastRow = FirstRow;
    }

    var MaxTopBorder     = this.MaxTopBorder;
    var MaxBotBorder     = this.MaxBotBorder;
    var MaxBotMargin     = this.MaxBotMargin;

    var StartPos = this.Pages[CurPage];
    if (true === this.Check_EmptyPages(CurPage - 1))
        this.HeaderInfo.PageIndex = -1;

    var Page = this.Pages[CurPage];
    var TempMaxTopBorder = Page.MaxTopBorder;

    var Y = StartPos.Y;
    var TableHeight = 0;

    var TableBorders = this.Get_Borders();

    var X_max = -1;
    var X_min = -1;
    if ( this.HeaderInfo.Count > 0 && this.HeaderInfo.PageIndex != -1 && CurPage > this.HeaderInfo.PageIndex )
    {
        this.HeaderInfo.Pages[CurPage] = {};
        this.HeaderInfo.Pages[CurPage].RowsInfo = [];
        var HeaderPage = this.HeaderInfo.Pages[CurPage];

        // Рисуем ли заголовок на данной странице
        HeaderPage.Draw = true;

        // Скопируем целиком строки
        HeaderPage.Rows = [];

        // Временно отключаем регистрацию новых классов
        AscCommon.g_oTableId.m_bTurnOff = true;
        AscCommon.History.TurnOff();

        var aContentDrawings = [];
        for ( var Index = 0; Index < this.HeaderInfo.Count; Index++ )
        {
            HeaderPage.Rows[Index] = this.Content[Index].Copy(this);
            HeaderPage.Rows[Index].Index = Index;
            for(var CellIndex = 0; CellIndex < HeaderPage.Rows[Index].Content.length; ++CellIndex)
            {
                HeaderPage.Rows[Index].Content[CellIndex].Content.GetAllDrawingObjects(aContentDrawings);
            }
        }
        for(var DrawingIndex = 0; DrawingIndex < aContentDrawings.length; ++DrawingIndex)
        {
            if(aContentDrawings[DrawingIndex] && aContentDrawings[DrawingIndex].GraphicObj)
            {
                aContentDrawings[DrawingIndex].GraphicObj.recalculate();
                if(aContentDrawings[DrawingIndex].GraphicObj.recalculateText)
                {
                    aContentDrawings[DrawingIndex].GraphicObj.recalculateText();
                }
            }
        }

        AscCommon.g_oTableId.m_bTurnOff = false;
        AscCommon.History.TurnOn();

        var bHeaderNextPage = false;
        for ( var CurRow = 0; CurRow < this.HeaderInfo.Count; CurRow++  )
        {
            HeaderPage.RowsInfo[CurRow] = {};
            HeaderPage.RowsInfo[CurRow].Y               = 0;
            HeaderPage.RowsInfo[CurRow].H               = 0;
            HeaderPage.RowsInfo[CurRow].TopDy           = 0;
            HeaderPage.RowsInfo[CurRow].MaxTopBorder    = 0;
            HeaderPage.RowsInfo[CurRow].TableRowsBottom = 0;

            var Row         = HeaderPage.Rows[CurRow];
            var CellsCount  = Row.Get_CellsCount();
            var CellSpacing = Row.Get_CellSpacing();

            var BeforeInfo  = Row.Get_Before();
            var CurGridCol  = BeforeInfo.GridBefore;

            // Добавляем ширину верхней границы у текущей строки (используем MaxTopBorder самой таблицы)
            Y           += MaxTopBorder[CurRow];
            TableHeight += MaxTopBorder[CurRow];

            // Если таблица с расстоянием между ячейками, тогда добавляем его
            if ( 0 === CurRow )
            {
                if ( null != CellSpacing )
                {
                    var TableBorder_Top = this.Get_Borders().Top;
                    if ( border_Single === TableBorder_Top.Value )
                    {
                        Y           += TableBorder_Top.Size;
                        TableHeight += TableBorder_Top.Size;
                    }

                    Y           += CellSpacing;
                    TableHeight += CellSpacing;
                }
            }
            else
            {
                var PrevCellSpacing = HeaderPage.Rows[CurRow - 1].Get_CellSpacing();

                if ( null != CellSpacing && null != PrevCellSpacing )
                {
                    Y           += (PrevCellSpacing + CellSpacing) / 2;
                    TableHeight += (PrevCellSpacing + CellSpacing) / 2;
                }
                else if ( null != CellSpacing )
                {
                    Y           += CellSpacing / 2;
                    TableHeight += CellSpacing / 2;
                }
                else if ( null != PrevCellSpacing )
                {
                    Y           += PrevCellSpacing / 2;
                    TableHeight += PrevCellSpacing / 2;
                }
            }

            var Row_x_max = Row.Metrics.X_max;
            var Row_x_min = Row.Metrics.X_min;

            if ( -1 === X_min || Row_x_min < X_min )
                X_min = Row_x_min;

            if ( -1 === X_max || Row_x_max > X_max )
                X_max = Row_x_max;

            // Дополнительный параметр для случая, если данная строка начнется с новой страницы.
            // Мы запоминаем максимальное значение нижней границы(первой страницы (текущей)) у ячеек,
            // объединенных вертикально так, чтобы это объединение заканчивалось на данной строке.
            // И если данная строка начнется сразу с новой страницы (Pages > 0, FirstPage = false), тогда
            // мы должны данный параметр сравнить со значением нижней границы предыдущей строки.
            var MaxBotValue_vmerge = -1;

            var RowH = Row.Get_Height();
            var VerticallCells = [];

            for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
            {
                var Cell     = Row.Get_Cell( CurCell );
                var GridSpan = Cell.Get_GridSpan();
                var Vmerge   = Cell.Get_VMerge();
                var CellMar  = Cell.Get_Margins();

                Row.Update_CellInfo(CurCell);

                var CellMetrics = Row.Get_CellInfo( CurCell );

                var X_content_start = Page.X + CellMetrics.X_content_start;
                var X_content_end   = Page.X + CellMetrics.X_content_end;

                var Y_content_start = Y + CellMar.Top.W;
                var Y_content_end   = this.Pages[CurPage].YLimit;

                // TODO: При расчете YLimit для ячейки сделать учет толщины нижних
                //       границ ячейки и таблицы
                if ( null != CellSpacing )
                {
                    if ( this.Content.length - 1 === CurRow )
                        Y_content_end -= CellSpacing;
                    else
                        Y_content_end -= CellSpacing / 2;
                }

                var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan );
                var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1];
                Y_content_end -= BottomMargin;

                // Такие ячейки мы обсчитываем, если либо сейчас происходит переход на новую страницу, либо
                // это последняя ячейка в объединении.
                // Обсчет такик ячеек произошел ранее

                Cell.Temp.Y = Y_content_start;

                if ( VMergeCount > 1 )
                {
                    CurGridCol += GridSpan;
                    continue;
                }
                else
                {
                    // Возьмем верхнюю ячейку теккущего объединения
                    if ( vmerge_Restart != Vmerge )
                    {
                        // Найдем ячейку в самой таблице, а дальше по индексам ячейки и строки получим ее в скопированном заголовке
                        Cell    = this.Internal_Get_StartMergedCell( CurRow, CurGridCol, GridSpan );
                        var cIndex = Cell.Index;
                        var rIndex = Cell.Row.Index;

                        Cell = HeaderPage.Rows[rIndex].Get_Cell( cIndex );

                        CellMar = Cell.Get_Margins();

                        Y_content_start = Cell.Temp.Y + CellMar.Top.W;
                    }
                }

                if (true === Cell.Is_VerticalText())
                {
                    VerticallCells.push(Cell);
                    CurGridCol += GridSpan;
                    continue;
                }

                Cell.Content.Set_StartPage( CurPage );
                Cell.Content.Reset( X_content_start, Y_content_start, X_content_end, Y_content_end );
                Cell.Content.Set_ClipInfo(0, Page.X + CellMetrics.X_cell_start, Page.X + CellMetrics.X_cell_end);

                if ( recalcresult2_NextPage === Cell.Content.Recalculate_Page( 0, true ) )
                {
                    bHeaderNextPage = true;
                    break;
                }

                var CellContentBounds = Cell.Content.Get_PageBounds( 0, undefined, true );
                var CellContentBounds_Bottom = CellContentBounds.Bottom + BottomMargin;

                if ( undefined === HeaderPage.RowsInfo[CurRow].TableRowsBottom || HeaderPage.RowsInfo[CurRow].TableRowsBottom < CellContentBounds_Bottom )
                    HeaderPage.RowsInfo[CurRow].TableRowsBottom = CellContentBounds_Bottom;

                if ( vmerge_Continue === Vmerge )
                {
                    if ( -1 === MaxBotValue_vmerge || MaxBotValue_vmerge < CellContentBounds_Bottom )
                        MaxBotValue_vmerge = CellContentBounds_Bottom;
                }

                CurGridCol += GridSpan;
            }

            // Если заголовок целиком на странице не убирается, тогда мы его попросту не рисуем на данной странице
            if ( true === bHeaderNextPage )
            {
                Y = StartPos.Y;
                TableHeight = 0;
                HeaderPage.Draw = false;
                break;
            }


            // Здесь мы выставляем только начальную координату строки (для каждой страницы)
            // высоту строки(для каждой страницы) мы должны обсчитать после общего цикла, т.к.
            // в одной из следйющих строк может оказаться ячейка с вертикальным объединением,
            // захватывающим данную строку. Значит, ее содержимое может изменить высоту нашей строки.
            var TempY            = Y;
            var TempMaxTopBorder = MaxTopBorder[CurRow];

            if ( null != CellSpacing )
            {
                HeaderPage.RowsInfo[CurRow].Y            = TempY;
                HeaderPage.RowsInfo[CurRow].TopDy        = 0;
                HeaderPage.RowsInfo[CurRow].X0           = Row_x_min;
                HeaderPage.RowsInfo[CurRow].X1           = Row_x_max;
                HeaderPage.RowsInfo[CurRow].MaxTopBorder = TempMaxTopBorder;
                HeaderPage.RowsInfo[CurRow].MaxBotBorder = MaxBotBorder[CurRow];
            }
            else
            {
                HeaderPage.RowsInfo[CurRow].Y            = TempY - TempMaxTopBorder;
                HeaderPage.RowsInfo[CurRow].TopDy        = TempMaxTopBorder;
                HeaderPage.RowsInfo[CurRow].X0           = Row_x_min;
                HeaderPage.RowsInfo[CurRow].X1           = Row_x_max;
                HeaderPage.RowsInfo[CurRow].MaxTopBorder = TempMaxTopBorder;
                HeaderPage.RowsInfo[CurRow].MaxBotBorder = MaxBotBorder[CurRow];
            }

            var CellHeight = HeaderPage.RowsInfo[CurRow].TableRowsBottom - Y;

            // TODO: улучшить проверку на высоту строки (для строк разбитых на страницы)
            if (false === bHeaderNextPage && Asc.linerule_AtLeast === RowH.HRule && CellHeight < RowH.Value - MaxTopBorder[CurRow])
            {
                CellHeight = RowH.Value - MaxTopBorder[CurRow];
                HeaderPage.RowsInfo[CurRow].TableRowsBottom = Y + CellHeight;
            }

            // Рассчитываем ячейки с вертикальным текстом
            var CellsCount2 = VerticallCells.length;
            for (var TempCellIndex = 0; TempCellIndex < CellsCount2; TempCellIndex++)
            {
                var Cell       = VerticallCells[TempCellIndex];
                var CurCell    = Cell.Index;
                var GridSpan   = Cell.Get_GridSpan();
                var CurGridCol = Cell.Metrics.StartGridCol;

                // Возьмем верхнюю ячейку текущего объединения
                Cell = this.Internal_Get_StartMergedCell(CurRow, CurGridCol, GridSpan);
                var cIndex = Cell.Index;
                var rIndex = Cell.Row.Index;
                Cell = HeaderPage.Rows[rIndex].Get_Cell( cIndex );

                var CellMar     = Cell.Get_Margins();
                var CellMetrics = Cell.Row.Get_CellInfo(CurCell);

                var X_content_start = Page.X + CellMetrics.X_content_start;
                var X_content_end   = Page.X + CellMetrics.X_content_end;
                var Y_content_start = Cell.Temp.Y;
                var Y_content_end   = HeaderPage.RowsInfo[CurRow].TableRowsBottom;

                // TODO: При расчете YLimit для ячейки сделать учет толщины нижних
                //       границ ячейки и таблицы
                if (null != CellSpacing)
                {
                    if (this.Content.length - 1 === CurRow)
                        Y_content_end -= CellSpacing;
                    else
                        Y_content_end -= CellSpacing / 2;
                }

                var VMergeCount = this.Internal_GetVertMergeCount(CurRow, CurGridCol, GridSpan);
                var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1];
                Y_content_end -= BottomMargin;

                Cell.PagesCount = 1;
                Cell.Content.Set_StartPage(CurPage);
                Cell.Content.Reset(0, 0, Y_content_end - Y_content_start, 10000);
                Cell.Temp.X_start = X_content_start;
                Cell.Temp.Y_start = Y_content_start;
                Cell.Temp.X_end   = X_content_end;
                Cell.Temp.Y_end   = Y_content_end;

                Cell.Temp.X_cell_start = Page.X + CellMetrics.X_cell_start;
                Cell.Temp.X_cell_end   = Page.X + CellMetrics.X_cell_end;
                Cell.Temp.Y_cell_start = Y_content_start - CellMar.Top.W;
                Cell.Temp.Y_cell_end   = Y_content_end + BottomMargin;


                // Какие-то ячейки в строке могут быть не разбиты на строки, а какие то разбиты.
                // Здесь контролируем этот момент, чтобы у тех, которые не разбиты не вызывать
                // Recalculate_Page от несуществующих страниц.
                var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();
                if (0 === CellPageIndex)
                {
                    Cell.Content.Recalculate_Page(CellPageIndex, true);
                }
            }

            if ( null != CellSpacing )
                HeaderPage.RowsInfo[CurRow].H = CellHeight;
            else
                HeaderPage.RowsInfo[CurRow].H = CellHeight + TempMaxTopBorder;

            Y           += CellHeight;
            TableHeight += CellHeight;

            Row.Height   = CellHeight;

            Y           += MaxBotBorder[CurRow];
            TableHeight += MaxBotBorder[CurRow];

            // Сделаем вертикальное выравнивание ячеек в таблице. Делаем как Word, если ячейка разбилась на несколько
            // страниц, тогда вертикальное выравнивание применяем только к первой странице.
        }

        if ( false === bHeaderNextPage )
        {
            // Сделаем вертикальное выравнивание ячеек в таблице. Делаем как Word, если ячейка разбилась на несколько
            // страниц, тогда вертикальное выравнивание применяем только к первой странице.
            // Делаем это не в общем цикле, потому что объединенные вертикально ячейки могут вносить поправки в значения
            // this.TableRowsBottom, в последней строке.
            for ( var CurRow = 0; CurRow < this.HeaderInfo.Count; CurRow++ )
            {
                var Row = HeaderPage.Rows[CurRow];
                var CellsCount = Row.Get_CellsCount();
                for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
                {
                    var Cell = Row.Get_Cell( CurCell );
                    var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );

                    if ( VMergeCount > 1 )
                        continue;
                    else
                    {
                        var Vmerge = Cell.Get_VMerge();
                        // Возьмем верхнюю ячейку теккущего объединения
                        if ( vmerge_Restart != Vmerge )
                        {
                            Cell = this.Internal_Get_StartMergedCell( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );
                            var cIndex = Cell.Index;
                            var rIndex = Cell.Row.Index;

                            Cell = HeaderPage.Rows[rIndex].Get_Cell( cIndex );
                        }
                    }

                    var CellMar       = Cell.Get_Margins();
                    var VAlign        = Cell.Get_VAlign();
                    var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();

                    if ( CellPageIndex >= Cell.PagesCount )
                        continue;

                    // Для прилегания к верху или для второй страницы ничего не делаем (так изначально рассчитывалось)
                    if ( vertalignjc_Top === VAlign || CellPageIndex > 1 )
                    {
                        Cell.Temp.Y_VAlign_offset[CellPageIndex] = 0;
                        continue;
                    }

                    // Рассчитаем имеющуюся в распоряжении высоту ячейки
                    var TempCurRow = Cell.Row.Index;
                    var TempCellSpacing = HeaderPage.Rows[TempCurRow].Get_CellSpacing();
                    var Y_0 = HeaderPage.RowsInfo[TempCurRow].Y;

                    if ( null === TempCellSpacing )
                        Y_0 += MaxTopBorder[TempCurRow];

                    Y_0 += CellMar.Top.W;

                    var Y_1 = HeaderPage.RowsInfo[CurRow].TableRowsBottom - CellMar.Bottom.W;
                    var CellHeight = Y_1 - Y_0;

                    var CellContentBounds = Cell.Content.Get_PageBounds( CellPageIndex, CellHeight, true );
                    var ContentHeight = CellContentBounds.Bottom - CellContentBounds.Top;

                    var Dy = 0;

                    if (true === Cell.Is_VerticalText())
                    {
                        var CellMetrics = Cell.Row.Get_CellInfo(Cell.Index);
                        CellHeight = CellMetrics.X_cell_end - CellMetrics.X_cell_start - CellMar.Left.W - CellMar.Right.W;
                    }

                    if ( CellHeight - ContentHeight > 0.001 )
                    {
                        if ( vertalignjc_Bottom === VAlign )
                            Dy = CellHeight - ContentHeight;
                        else if ( vertalignjc_Center === VAlign )
                            Dy = (CellHeight - ContentHeight) / 2;

                        Cell.Content.Shift( CellPageIndex, 0, Dy );
                    }

                    Cell.Temp.Y_VAlign_offset[CellPageIndex] = Dy;
                }
            }
        }
    }
    else
    {
        this.HeaderInfo.Pages[CurPage] = {};
        this.HeaderInfo.Pages[CurPage].Draw = false;
    }

	var bResetFootnotes = true;
	var oFootnotesObject, nFootnotesHeight = 0, nSavedY = 0, nSavedTableHeight = 0;
    var bNextPage = false;
    for (var CurRow = FirstRow; CurRow < this.Content.length; ++CurRow)
    {
		if (true === bResetFootnotes && oFootnotes)
		{
			oFootnotesObject  = oFootnotes.SaveRecalculateObject(nPageAbs, nColumnAbs);
			nFootnotesHeight  = oFootnotes.GetHeight(nPageAbs, nColumnAbs);
			nSavedY           = Y;
			nSavedTableHeight = TableHeight;

			this.Pages[CurPage].FootnotesH = nFootnotesHeight;
		}

		if ((0 === CurRow && true === this.Check_EmptyPages(CurPage - 1)) || CurRow != FirstRow || (CurRow === FirstRow && true === ResetStartElement))
        {
            this.RowsInfo[CurRow] = {};
            this.RowsInfo[CurRow].Pages        = 1;
            this.RowsInfo[CurRow].Y            = [];
            this.RowsInfo[CurRow].H            = [];
            this.RowsInfo[CurRow].TopDy        = [];
            this.RowsInfo[CurRow].MaxTopBorder = [];
            this.RowsInfo[CurRow].FirstPage    = true;
            this.RowsInfo[CurRow].StartPage    = CurPage;
            this.TableRowsBottom[CurRow]       = [];
        }
        else
        {
            this.RowsInfo[CurRow].Pages++;
        }

        this.TableRowsBottom[CurRow][CurPage] = Y;

        var Row         = this.Content[CurRow];
        var CellsCount  = Row.Get_CellsCount();
        var CellSpacing = Row.Get_CellSpacing();

        var BeforeInfo  = Row.Get_Before();
        var AfterInfo   = Row.Get_After();
        var CurGridCol  = BeforeInfo.GridBefore;

        // Добавляем ширину верхней границы у текущей строки
        Y           += MaxTopBorder[CurRow];
        TableHeight += MaxTopBorder[CurRow];

        // Если таблица с расстоянием между ячейками, тогда добавляем его
        if (FirstRow === CurRow)
        {
            if (null != CellSpacing)
            {
                var TableBorder_Top = this.Get_Borders().Top;
                if (border_Single === TableBorder_Top.Value)
                {
                    Y           += TableBorder_Top.Size;
                    TableHeight += TableBorder_Top.Size;
                }

                if (true === this.HeaderInfo.Pages[CurPage].Draw || (0 === CurRow && (0 === CurPage || (1 === CurPage && false === this.RowsInfo[0].FirstPage))))
                {
                    Y           += CellSpacing;
                    TableHeight += CellSpacing;
                }
                else
                {
                    Y           += CellSpacing / 2;
                    TableHeight += CellSpacing / 2;
                }
            }
        }
        else
        {
            var PrevCellSpacing = this.Content[CurRow - 1].Get_CellSpacing();

            if (null != CellSpacing && null != PrevCellSpacing)
            {
                Y           += (PrevCellSpacing + CellSpacing) / 2;
                TableHeight += (PrevCellSpacing + CellSpacing) / 2;
            }
            else if (null != CellSpacing)
            {
                Y           += CellSpacing / 2;
                TableHeight += CellSpacing / 2;
            }
            else if (null != PrevCellSpacing)
            {
                Y           += PrevCellSpacing / 2;
                TableHeight += PrevCellSpacing / 2;
            }
        }

        var Row_x_max = Row.Metrics.X_max;
        var Row_x_min = Row.Metrics.X_min;

        if (-1 === X_min || Row_x_min < X_min)
            X_min = Row_x_min;

        if (-1 === X_max || Row_x_max > X_max)
            X_max = Row_x_max;

		var MaxTopMargin = 0;

		for (var CurCell = 0; CurCell < CellsCount; CurCell++)
		{
			var Cell    = Row.Get_Cell(CurCell);
			var Vmerge  = Cell.Get_VMerge();
			var CellMar = Cell.Get_Margins();
			if (vmerge_Restart === Vmerge && CellMar.Top.W > MaxTopMargin)
				MaxTopMargin = CellMar.Top.W;
		}

		var RowH = Row.Get_Height();
		var RowHValue = RowH.Value;
		// В данном значении не учитываются маргины
		RowHValue = RowH.Value + this.MaxBotMargin[CurRow] + MaxTopMargin;

		if (oFootnotes && (Asc.linerule_AtLeast === RowH.HRule || Asc.linerule_Exact == RowH.HRule))
		{
			oFootnotes.PushCellLimit(Y + RowHValue);
		}

        // Дополнительный параметр для случая, если данная строка начнется с новой страницы.
        // Мы запоминаем максимальное значение нижней границы(первой страницы (текущей)) у ячеек,
        // объединенных вертикально так, чтобы это объединение заканчивалось на данной строке.
        // И если данная строка начнется сразу с новой страницы (Pages > 0, FirstPage = false), тогда
        // мы должны данный параметр сравнить со значением нижней границы предыдущей строки.
        var MaxBotValue_vmerge = -1;

        var Merged_Cell  = [];
        var VerticallCells = [];
        var bAllCellsVertical = true;

        for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
        {
            var Cell     = Row.Get_Cell( CurCell );
            var GridSpan = Cell.Get_GridSpan();
            var Vmerge   = Cell.Get_VMerge();
            var CellMar  = Cell.Get_Margins();

            Row.Update_CellInfo(CurCell);

            var CellMetrics   = Row.Get_CellInfo( CurCell );

            var X_content_start = Page.X + CellMetrics.X_content_start;
            var X_content_end   = Page.X + CellMetrics.X_content_end;

            var Y_content_start = Y + CellMar.Top.W;
            var Y_content_end   = this.Pages[CurPage].YLimit - nFootnotesHeight;

            // TODO: При расчете YLimit для ячейки сделать учет толщины нижних
            //       границ ячейки и таблицы
            if ( null != CellSpacing )
            {
                if ( this.Content.length - 1 === CurRow )
                    Y_content_end -= CellSpacing;
                else
                    Y_content_end -= CellSpacing / 2;
            }

            var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan );
            var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1];
            Y_content_end -= BottomMargin;

            // Такие ячейки мы обсчитываем, если либо сейчас происходит переход на новую страницу, либо
            // это последняя ячейка в объединении.
            // Обсчет такик ячеек произошел ранее

            Cell.Temp.Y = Y_content_start;

            if ( VMergeCount > 1 )
            {
                CurGridCol += GridSpan;
                Merged_Cell.push( Cell );
                continue;
            }
            else
            {
                // Возьмем верхнюю ячейку теккущего объединения
                if ( vmerge_Restart != Vmerge )
                {
                    Cell = this.Internal_Get_StartMergedCell( CurRow, CurGridCol, GridSpan );
                    CellMar = Cell.Get_Margins();

                    Y_content_start = Cell.Temp.Y + CellMar.Top.W;
                }
            }

            if (true === Cell.Is_VerticalText())
            {
                VerticallCells.push(Cell);
                CurGridCol += GridSpan;
                continue;
            }

            bAllCellsVertical = false;

            var bCanShift = false;
            var ShiftDy   = 0;
            var ShiftDx   = 0;

            if ((0 === Cell.Row.Index && true === this.Check_EmptyPages(CurPage - 1)) || Cell.Row.Index > FirstRow || (Cell.Row.Index === FirstRow && true === ResetStartElement))
            {
                Cell.Content.Set_StartPage( CurPage );

                if (  true === this.Is_Inline() && 1 === Cell.PagesCount && 1 === Cell.Content.Pages.length && true != this.RecalcInfo.Check_Cell( Cell ) )
                {
                    var X_content_start_old  = Cell.Content.Pages[0].X;
                    var X_content_end_old    = Cell.Content.Pages[0].XLimit;
                    var Y_content_height_old = Cell.Content.Pages[0].Bounds.Bottom - Cell.Content.Pages[0].Bounds.Top;

                    // Проверим по X, Y.
					// var nFootnotesHeight = 0;
					// if (oTopDocument && isTopLogicDocument)
						// nFootnotesHeight = oTopDocument.Footnotes.GetHeight(nPageAbs, nColumnAbs);
					//
                    if ( Math.abs( X_content_start - X_content_start_old ) < 0.001 && Math.abs( X_content_end_old - X_content_end ) < 0.001 && Y_content_start + Y_content_height_old < Y_content_end )
                    {
                        bCanShift = true;
                        ShiftDy   = -Cell.Content.Pages[0].Y + Y_content_start;

						// Если в ячейке есть ссылки на сноски, тогда такую ячейку нужно пересчитывать
						var arrFootnotes = Cell.Content.Get_FootnotesList(null, null);
						if (arrFootnotes && arrFootnotes.length > 0)
							bCanShift = false;
                    }
                }

                Cell.PagesCount = 1;
                Cell.Content.Reset(X_content_start, Y_content_start, X_content_end, Y_content_end);
            }

            // Какие-то ячейки в строке могут быть не разбиты на строки, а какие то разбиты.
            // Здесь контролируем этот момент, чтобы у тех, которые не разбиты не вызывать
            // Recalculate_Page от несуществующих страниц.
            var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();
            Cell.Content.Set_ClipInfo(CellPageIndex, Page.X + CellMetrics.X_cell_start, Page.X + CellMetrics.X_cell_end);
            if ( CellPageIndex < Cell.PagesCount )
            {
                if ( true === bCanShift )
                {
                    Cell.Content.Shift( 0, ShiftDx, ShiftDy );
                    Cell.Content.UpdateEndInfo();
                }
                else
                {
					var RecalcResult = Cell.Content.Recalculate_Page(CellPageIndex, true);
					if (recalcresult2_CurPage & RecalcResult)
					{
						var _RecalcResult = recalcresult_CurPage;

						if (RecalcResult & recalcresultflags_Column)
							_RecalcResult |= recalcresultflags_Column;

						if (RecalcResult & recalcresultflags_Footnotes)
							_RecalcResult |= recalcresultflags_Footnotes;

						this.TurnOffRecalc = false;
						return _RecalcResult;
					}
					else if (recalcresult2_NextPage & RecalcResult)
					{
						Cell.PagesCount = Cell.Content.Pages.length + 1;
						bNextPage       = true;
					}
					else if (recalcresult2_End & RecalcResult)
					{
						// Ничего не делаем
					}
                }

                var CellContentBounds = Cell.Content.Get_PageBounds( CellPageIndex, undefined, true );
                var CellContentBounds_Bottom = CellContentBounds.Bottom + BottomMargin;

                if ( undefined === this.TableRowsBottom[CurRow][CurPage] || this.TableRowsBottom[CurRow][CurPage] < CellContentBounds_Bottom )
                    this.TableRowsBottom[CurRow][CurPage] = CellContentBounds_Bottom;

                if ( vmerge_Continue === Vmerge )
                {
                    if ( -1 === MaxBotValue_vmerge || MaxBotValue_vmerge < CellContentBounds_Bottom )
                        MaxBotValue_vmerge = CellContentBounds_Bottom;
                }
            }

            CurGridCol += GridSpan;
        }

		if (oFootnotes && (Asc.linerule_AtLeast === RowH.HRule || Asc.linerule_Exact == RowH.HRule))
		{
			oFootnotes.PopCellLimit();
		}

        var nCurFootnotesHeight = oFootnotes ? oFootnotes.GetHeight(nPageAbs, nColumnAbs) : 0;
        if (oFootnotes && nCurFootnotesHeight > nFootnotesHeight + 0.001)
		{
			this.Pages[CurPage].FootnotesH = nCurFootnotesHeight;

			nFootnotesHeight = nCurFootnotesHeight;
			bResetFootnotes  = false;
			Y                = nSavedY;
			TableHeight      = nSavedTableHeight;

			oFootnotes.LoadRecalculateObject(nPageAbs, nColumnAbs, oFootnotesObject);

			CurRow--;
			continue;
		}
		else
		{
			bResetFootnotes = true;
		}

        if (undefined === this.TableRowsBottom[CurRow][CurPage])
            this.TableRowsBottom[CurRow][CurPage] = Y;

        // Если в строке все ячейки с вертикальным выравниванием
        if (true === bAllCellsVertical && Asc.linerule_Auto === RowH.HRule)
            this.TableRowsBottom[CurRow][CurPage] = Y + 4.5 + this.MaxBotMargin[CurRow] + MaxTopMargin;

        if ((Asc.linerule_AtLeast === RowH.HRule || Asc.linerule_Exact == RowH.HRule) && Y + RowHValue > Y_content_end && ((0 === CurRow && 0 === CurPage && (null !== this.Get_DocumentPrev() || true === this.Parent.Is_TableCellContent())) || CurRow != FirstRow))
        {
            bNextPage = true;

            for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
            {
                var Cell   = Row.Get_Cell( CurCell );
                var Vmerge = Cell.Get_VMerge();

                var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );

                // Проверяем только начальные ячейки вертикального объединения..
                if ( vmerge_Continue === Vmerge || VMergeCount > 1 )
                    continue;

                Cell.Content.StartFromNewPage();
                Cell.PagesCount = 2;
            }
        }

        // Данная строка разбилась на несколько страниц. Нам нужно сделать несколько дополнительных действий:
        // 1. Проверяем есть ли хоть какой-либо контент данной строки на первой странице, т.е. реально данная
        //    строка начинается со 2-ой страницы.
        // 2. Пересчитать все смерженные вертикально ячейки, которые также разбиваются на несколько страниц,
        //    но у которых вертикальное объединение не заканчивается на данной странице.
        if ( true === bNextPage )
        {
            var bContentOnFirstPage   = false;
            var bNoContentOnFirstPage = false;
            for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
            {
                var Cell   = Row.Get_Cell( CurCell );
                var Vmerge = Cell.Get_VMerge();

                var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );

                // Проверяем только начальные ячейки вертикального объединения..
                if ( vmerge_Continue === Vmerge || VMergeCount > 1 )
                    continue;

                if (true === Cell.Is_VerticalText() || true === Cell.Content_Is_ContentOnFirstPage())
                {
                    bContentOnFirstPage = true;
                }
                else
                {
                    bNoContentOnFirstPage = true;
                }
            }

            if ( true === bContentOnFirstPage && true === bNoContentOnFirstPage )
            {
                for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
                {
                    var Cell   = Row.Get_Cell( CurCell );
                    var Vmerge = Cell.Get_VMerge();

                    var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );

                    // Проверяем только начальные ячейки вертикального объединения..
                    if ( vmerge_Continue === Vmerge || VMergeCount > 1 )
                        continue;

                    Cell.Content.StartFromNewPage();
                    Cell.PagesCount = 2;
                }

                bContentOnFirstPage = false;
            }

            this.RowsInfo[CurRow].FirstPage = bContentOnFirstPage;

            // Не сраниваем MaxBotValue_vmerge с -1, т.к. значения в this.TableRowsBottom в любом случае неотрицательные
            if ( 0 != CurRow && false === this.RowsInfo[CurRow].FirstPage )
            {
                if ( this.TableRowsBottom[CurRow - 1][CurPage] < MaxBotValue_vmerge )
                {
                    // Поскольку мы правим настройку не текущей строки, надо подправить и
                    // запись о рассчитанной высоте строки
                    var Diff = MaxBotValue_vmerge - this.TableRowsBottom[CurRow - 1][CurPage];
                    this.TableRowsBottom[CurRow - 1][CurPage] = MaxBotValue_vmerge;
                    this.RowsInfo[CurRow - 1].H[CurPage] += Diff;
                }
            }

            // Здесь мы должны рассчитать ячейки, которые попали в вертикальное объединение и из-за этого не были рассчитаны
            var CellsCount2 = Merged_Cell.length;
            for (var TempCellIndex = 0; TempCellIndex < CellsCount2; TempCellIndex++)
            {
                var Cell = Merged_Cell[TempCellIndex];
                var CurCell = Cell.Index;
                var GridSpan = Cell.Get_GridSpan();
                var CurGridCol = Cell.Metrics.StartGridCol;

                // Возьмем верхнюю ячейку теккущего объединения
                Cell = this.Internal_Get_StartMergedCell(CurRow, CurGridCol, GridSpan);

                if (true === Cell.Is_VerticalText())
                {
                    VerticallCells.push(Cell);
                    CurGridCol += GridSpan;
                    continue;
                }

                var CellMar = Cell.Get_Margins();
                var CellMetrics = Row.Get_CellInfo(CurCell);

                var X_content_start = Page.X + CellMetrics.X_content_start;
                var X_content_end   = Page.X + CellMetrics.X_content_end;

                // Если в текущей строке на данной странице не убралось ничего из других ячеек, тогда
                // рассчитываем вертикально объединенные ячейки до начала данной строки.
                var Y_content_start = Cell.Temp.Y;
                var Y_content_end   = false === bContentOnFirstPage ? Y : this.Pages[CurPage].YLimit;

                // TODO: При расчете YLimit для ячейки сделать учет толщины нижних
                //       границ ячейки и таблицы
                if (null != CellSpacing)
                {
                    if (this.Content.length - 1 === CurRow)
                        Y_content_end -= CellSpacing;
                    else
                        Y_content_end -= CellSpacing / 2;
                }

                var VMergeCount = this.Internal_GetVertMergeCount(CurRow, CurGridCol, GridSpan);
                var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1];
                Y_content_end -= BottomMargin;

                if ((0 === Cell.Row.Index && 0 === CurPage) || Cell.Row.Index > FirstRow)
                {
                    Cell.PagesCount = 1;
                    Cell.Content.Set_StartPage(CurPage);
                    Cell.Content.Reset(X_content_start, Y_content_start, X_content_end, Y_content_end);
                }

                // Какие-то ячейки в строке могут быть не разбиты на строки, а какие то разбиты.
                // Здесь контролируем этот момент, чтобы у тех, которые не разбиты не вызывать
                // Recalculate_Page от несуществующих страниц.
                var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();
                if (CellPageIndex < Cell.PagesCount)
                {
                    if (recalcresult2_NextPage === Cell.Content.Recalculate_Page(CellPageIndex, true))
                    {
                        Cell.PagesCount = Cell.Content.Pages.length + 1;
                        bNextPage = true;
                    }

                    var CellContentBounds = Cell.Content.Get_PageBounds(CellPageIndex, undefined, true);
                    var CellContentBounds_Bottom = CellContentBounds.Bottom + BottomMargin;

                    if (0 != CurRow && false === this.RowsInfo[CurRow].FirstPage)
                    {
                        if (this.TableRowsBottom[CurRow - 1][CurPage] < CellContentBounds_Bottom)
                        {
                            // Поскольку мы правим настройку не текущей строки, надо подправить и
                            // запись о рассчитанной высоте строки
                            var Diff = CellContentBounds_Bottom - this.TableRowsBottom[CurRow - 1][CurPage];
                            this.TableRowsBottom[CurRow - 1][CurPage] = CellContentBounds_Bottom;
                            this.RowsInfo[CurRow - 1].H[CurPage] += Diff;
                        }
                    }
                    else
                    {
                        if (undefined === this.TableRowsBottom[CurRow][CurPage] || this.TableRowsBottom[CurRow][CurPage] < CellContentBounds_Bottom)
                            this.TableRowsBottom[CurRow][CurPage] = CellContentBounds_Bottom;
                    }
                }

                CurGridCol += GridSpan;
            }


            // Еще раз обновляем параметр, есть ли текст на первой странице
            bContentOnFirstPage = false;
            for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
            {
                var Cell   = Row.Get_Cell( CurCell );
                var Vmerge = Cell.Get_VMerge();

                // Проверяем только начальные ячейки вертикального объединения..
                if ( vmerge_Continue === Vmerge )
                    continue;

                if (true === Cell.Is_VerticalText())
                    continue;

                if ( true === Cell.Content_Is_ContentOnFirstPage() )
                {
                    bContentOnFirstPage = true;
                    break;
                }
            }

            this.RowsInfo[CurRow].FirstPage = bContentOnFirstPage;
        }

        // Выставляем так, чтобы высота была равна 0
        if (true !== this.RowsInfo[CurRow].FirstPage && CurPage === this.RowsInfo[CurRow].StartPage)
            this.TableRowsBottom[CurRow][CurPage] = Y;

        // Здесь мы выставляем только начальную координату строки (для каждой страницы)
        // высоту строки(для каждой страницы) мы должны обсчитать после общего цикла, т.к.
        // в одной из следйющих строк может оказаться ячейка с вертикальным объединением,
        // захватывающим данную строку. Значит, ее содержимое может изменить высоту нашей строки.
        var TempY            = Y;
        var TempMaxTopBorder = MaxTopBorder[CurRow];

        if ( null != CellSpacing )
        {
            this.RowsInfo[CurRow].Y[CurPage]            = TempY;
            this.RowsInfo[CurRow].TopDy[CurPage]        = 0;
            this.RowsInfo[CurRow].X0                    = Row_x_min;
            this.RowsInfo[CurRow].X1                    = Row_x_max;
            this.RowsInfo[CurRow].MaxTopBorder[CurPage] = TempMaxTopBorder;
            this.RowsInfo[CurRow].MaxBotBorder          = MaxBotBorder[CurRow];
        }
        else
        {
            this.RowsInfo[CurRow].Y[CurPage]            = TempY - TempMaxTopBorder;
            this.RowsInfo[CurRow].TopDy[CurPage]        = TempMaxTopBorder;
            this.RowsInfo[CurRow].X0                    = Row_x_min;
            this.RowsInfo[CurRow].X1                    = Row_x_max;
            this.RowsInfo[CurRow].MaxTopBorder[CurPage] = TempMaxTopBorder;
            this.RowsInfo[CurRow].MaxBotBorder          = MaxBotBorder[CurRow];
        }

        var CellHeight = this.TableRowsBottom[CurRow][CurPage] - Y;

		// TODO: улучшить проверку на высоту строки (для строк разбитых на страницы)
		// Условие Y + RowHValue < Y_content_end добавлено из-за сносок.
		if (false === bNextPage && Asc.linerule_AtLeast === RowH.HRule && CellHeight < RowHValue && Y + RowHValue < Y_content_end)
		{
			CellHeight                            = RowHValue;
			this.TableRowsBottom[CurRow][CurPage] = Y + CellHeight;
		}

        // Рассчитываем ячейки с вертикальным текстом
        var CellsCount2 = VerticallCells.length;
        for (var TempCellIndex = 0; TempCellIndex < CellsCount2; TempCellIndex++)
        {
            var Cell       = VerticallCells[TempCellIndex];
            var CurCell    = Cell.Index;
            var GridSpan   = Cell.Get_GridSpan();
            var CurGridCol = Cell.Metrics.StartGridCol;

            // Возьмем верхнюю ячейку текущего объединения
            Cell = this.Internal_Get_StartMergedCell(CurRow, CurGridCol, GridSpan);

            var CellMar     = Cell.Get_Margins();
            var CellMetrics = Cell.Row.Get_CellInfo(Cell.Index);

            var X_content_start = Page.X + CellMetrics.X_content_start;
            var X_content_end   = Page.X + CellMetrics.X_content_end;
            var Y_content_start = Cell.Temp.Y;
            var Y_content_end   = this.TableRowsBottom[CurRow][CurPage];

            // TODO: При расчете YLimit для ячейки сделать учет толщины нижних
            //       границ ячейки и таблицы
            if (null != CellSpacing)
            {
                if (this.Content.length - 1 === CurRow)
                    Y_content_end -= CellSpacing;
                else
                    Y_content_end -= CellSpacing / 2;
            }

            var VMergeCount = this.Internal_GetVertMergeCount(CurRow, CurGridCol, GridSpan);
            var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1];
            Y_content_end -= BottomMargin;

            if ((0 === Cell.Row.Index && true === this.Check_EmptyPages(CurPage - 1)) || Cell.Row.Index > FirstRow || (Cell.Row.Index === FirstRow && true === ResetStartElement))
            {
                // TODO: Здесь надо сделать, чтобы ячейка не билась на страницы
                Cell.PagesCount = 1;
                Cell.Content.Set_StartPage(CurPage);
                Cell.Content.Reset(0, 0, Y_content_end - Y_content_start, 10000);
                Cell.Temp.X_start = X_content_start;
                Cell.Temp.Y_start = Y_content_start;
                Cell.Temp.X_end   = X_content_end;
                Cell.Temp.Y_end   = Y_content_end;

                Cell.Temp.X_cell_start = Page.X + CellMetrics.X_cell_start;
                Cell.Temp.X_cell_end   = Page.X + CellMetrics.X_cell_end;
                Cell.Temp.Y_cell_start = Y_content_start - CellMar.Top.W;
                Cell.Temp.Y_cell_end   = Y_content_end + BottomMargin;
            }

            // Какие-то ячейки в строке могут быть не разбиты на строки, а какие то разбиты.
            // Здесь контролируем этот момент, чтобы у тех, которые не разбиты не вызывать
            // Recalculate_Page от несуществующих страниц.
            var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();
            if (0 === CellPageIndex)
            {
                Cell.Content.Recalculate_Page(CellPageIndex, true);
            }
        }

		// Еще раз проверим, были ли сноски
		var nCurFootnotesHeight = oFootnotes ? oFootnotes.GetHeight(nPageAbs, nColumnAbs) : 0;
		if (oFootnotes && nCurFootnotesHeight > nFootnotesHeight + 0.001)
		{
			this.Pages[CurPage].FootnotesH = nCurFootnotesHeight;

			nFootnotesHeight = nCurFootnotesHeight;
			bResetFootnotes  = false;
			Y                = nSavedY;
			TableHeight      = nSavedTableHeight;

			oFootnotes.LoadRecalculateObject(nPageAbs, nColumnAbs, oFootnotesObject);

			CurRow--;
			continue;
		}
		else
		{
			bResetFootnotes = true;
		}

        if ( null != CellSpacing )
            this.RowsInfo[CurRow].H[CurPage] = CellHeight;
        else
            this.RowsInfo[CurRow].H[CurPage] = CellHeight + TempMaxTopBorder;

        Y           += CellHeight;
        TableHeight += CellHeight;

        Row.Height   = CellHeight;

        Y           += MaxBotBorder[CurRow];
        TableHeight += MaxBotBorder[CurRow];

        if ( this.Content.length - 1 === CurRow )
        {
            if ( null != CellSpacing )
            {
                TableHeight += CellSpacing;

                var TableBorder_Bottom = this.Get_Borders().Bottom;
                if ( border_Single === TableBorder_Bottom.Value )
                    TableHeight += TableBorder_Bottom.Size;
            }
        }

        if ( true === bNextPage )
        {
            LastRow = CurRow;
            this.Pages[CurPage].LastRow = CurRow;

            if  ( -1 === this.HeaderInfo.PageIndex && this.HeaderInfo.Count > 0 && CurRow >= this.HeaderInfo.Count )
                this.HeaderInfo.PageIndex = CurPage;

            break;
        }
        else if ( this.Content.length - 1 === CurRow )
        {
            LastRow = this.Content.length - 1;
            this.Pages[CurPage].LastRow = this.Content.length - 1;
        }
    }

    // Сделаем вертикальное выравнивание ячеек в таблице. Делаем как Word, если ячейка разбилась на несколько
    // страниц, тогда вертикальное выравнивание применяем только к первой странице.
    // Делаем это не в общем цикле, потому что объединенные вертикально ячейки могут вносить поправки в значения
    // this.TableRowsBottom, в последней строке.
    for ( var CurRow = FirstRow; CurRow <= LastRow; CurRow++ )
    {
        var Row = this.Content[CurRow];
        var CellsCount = Row.Get_CellsCount();
        for ( var CurCell = 0; CurCell < CellsCount; CurCell++ )
        {
            var Cell = Row.Get_Cell( CurCell );
            var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );

            if ( VMergeCount > 1 && CurRow != LastRow )
                continue;
            else
            {
                var Vmerge = Cell.Get_VMerge();
                // Возьмем верхнюю ячейку теккущего объединения
                if ( vmerge_Restart != Vmerge )
                {
                    Cell = this.Internal_Get_StartMergedCell( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() );
                }
            }

            var CellMar       = Cell.Get_Margins();
            var VAlign        = Cell.Get_VAlign();
            var CellPageIndex = CurPage - Cell.Content.Get_StartPage_Relative();

            if ( CellPageIndex >= Cell.PagesCount )
                continue;

            // Рассчитаем имеющуюся в распоряжении высоту ячейки
            var TempCurRow = Cell.Row.Index;

            // Для прилегания к верху или для второй страницы ничего не делаем (так изначально рассчитывалось)
            if ( vertalignjc_Top === VAlign || CellPageIndex > 1 || (1 === CellPageIndex && true === this.RowsInfo[TempCurRow].FirstPage ) )
            {
                Cell.Temp.Y_VAlign_offset[CellPageIndex] = 0;
                continue;
            }

            var TempCellSpacing = this.Content[TempCurRow].Get_CellSpacing();
            var Y_0 = this.RowsInfo[TempCurRow].Y[CurPage];

            if ( null === TempCellSpacing )
                Y_0 += MaxTopBorder[TempCurRow];

            Y_0 += CellMar.Top.W;

            var Y_1 = this.TableRowsBottom[CurRow][CurPage] - CellMar.Bottom.W;
            var CellHeight = Y_1 - Y_0;

            var CellContentBounds = Cell.Content.Get_PageBounds( CellPageIndex, CellHeight, true );
            var ContentHeight = CellContentBounds.Bottom - CellContentBounds.Top;

            var Dy = 0;

            if (true === Cell.Is_VerticalText())
            {
                var CellMetrics = Row.Get_CellInfo(CurCell);
                CellHeight = CellMetrics.X_cell_end - CellMetrics.X_cell_start - CellMar.Left.W - CellMar.Right.W;
            }

            if (CellHeight - ContentHeight > 0.001)
            {
                if (vertalignjc_Bottom === VAlign)
                    Dy = CellHeight - ContentHeight;
                else if (vertalignjc_Center === VAlign)
                    Dy = (CellHeight - ContentHeight) / 2;

                Cell.Content.Shift(CellPageIndex, 0, Dy);
            }

            Cell.Temp.Y_VAlign_offset[CellPageIndex] = Dy;
        }
    }


    // Просчитаем нижнюю границу таблицы на данной странице
    var CurRow = LastRow;
    if ( 0 === CurRow && false === this.RowsInfo[CurRow].FirstPage && 0 === CurPage )
    {
        // Таблица сразу переносится на следующую страницу
        this.Pages[0].MaxBotBorder = 0;
        this.Pages[0].BotBorders   = [];
    }
    else
    {
        // Если последняя строка на данной странице не имеет контента, тогда рассчитываем
        // границу у предыдущей строки.
        if ( false === this.RowsInfo[CurRow].FirstPage && CurPage === this.RowsInfo[CurRow].StartPage )
            CurRow--;

        var MaxBotBorder = 0;
        var BotBorders   = [];

        if (CurRow >= this.Pages[CurPage].FirstRow)
        {
            // Для ряда CurRow вычисляем нижнюю границу
            if (this.Content.length - 1 === CurRow)
            {
                // Для последнего ряда уже есть готовые нижние границы
                var Row        = this.Content[CurRow];
                var CellsCount = Row.Get_CellsCount();
                for (var CurCell = 0; CurCell < CellsCount; CurCell++)
                {
                    var Cell = Row.Get_Cell(CurCell);
                    if (vmerge_Continue === Cell.Get_VMerge())
                        Cell = this.Internal_Get_StartMergedCell(CurRow, Row.Get_CellInfo(CurCell).StartGridCol, Cell.Get_GridSpan());

                    var Border_Info = Cell.Get_BorderInfo().Bottom;

                    for (var BorderId = 0; BorderId < Border_Info.length; BorderId++)
                    {
                        var Border = Border_Info[BorderId];
                        if (border_Single === Border.Value && MaxBotBorder < Border.Size)
                            MaxBotBorder = Border.Size;

                        BotBorders.push(Border);
                    }
                }
            }
            else
            {
                var Row         = this.Content[CurRow];
                var CellSpacing = Row.Get_CellSpacing();
                var CellsCount  = Row.Get_CellsCount();

                if (null != CellSpacing)
                {
                    // BotBorders можно не заполнять, т.к. у каждой ячейки своя граница,
                    // нам надо только посчитать максимальную толщину.
                    for (var CurCell = 0; CurCell < CellsCount; CurCell++)
                    {
                        var Cell   = Row.Get_Cell(CurCell);
                        var Border = Cell.Get_Borders().Bottom;

                        if (border_Single === Border.Value && MaxBotBorder < Border.Size)
                            MaxBotBorder = Border.Size;
                    }
                }
                else
                {
                    // Сравниваем нижнюю границу ячейки и нижнюю границу таблицы
                    for (var CurCell = 0; CurCell < CellsCount; CurCell++)
                    {
                        var Cell = Row.Get_Cell(CurCell);

                        if (vmerge_Continue === Cell.Get_VMerge())
                        {
                            Cell = this.Internal_Get_StartMergedCell(CurRow, Row.Get_CellInfo(CurCell).StartGridCol, Cell.Get_GridSpan());
                            if (null === Cell)
                            {
                                BotBorders.push(TableBorders.Bottom);
                                continue;
                            }
                        }

                        var Border = Cell.Get_Borders().Bottom;

                        // Сравним границы
                        var Result_Border = this.Internal_CompareBorders(Border, TableBorders.Bottom, false, true);
                        if (border_Single === Result_Border.Value && MaxBotBorder < Result_Border.Size)
                            MaxBotBorder = Result_Border.Size;

                        BotBorders.push(Result_Border);
                    }
                }
            }
        }

        this.Pages[CurPage].MaxBotBorder = MaxBotBorder;
        this.Pages[CurPage].BotBorders   = BotBorders;
    }

    this.Pages[CurPage].Bounds.Bottom = this.Pages[CurPage].Bounds.Top + TableHeight;
    this.Pages[CurPage].Bounds.Left   = X_min + this.Pages[CurPage].X;
    this.Pages[CurPage].Bounds.Right  = X_max + this.Pages[CurPage].X;
    this.Pages[CurPage].Height        = TableHeight;

    if (true === bNextPage)
    {
        var LastRow = this.Pages[CurPage].LastRow;
        if (false === this.RowsInfo[LastRow].FirstPage)
            this.Pages[CurPage].LastRow = LastRow - 1;
        else
            this.Pages[CurPage].LastRowSplit = true;
    }

    this.TurnOffRecalc = false;

    this.Bounds = this.Pages[this.Pages.length - 1].Bounds;

    if ( true == bNextPage )
        return recalcresult_NextPage;
    else
        return recalcresult_NextElement;
};
CTable.prototype.private_RecalculatePositionY = function(CurPage)
{
    var PageLimits = this.Parent.Get_PageLimits(this.PageNum + CurPage);
    var PageFields = this.Parent.Get_PageFields(this.PageNum + CurPage);
    var LD_PageFields = this.LogicDocument.Get_PageFields(this.Get_StartPage_Absolute() + CurPage);
    var LD_PageLimits = this.LogicDocument.Get_PageLimits(this.Get_StartPage_Absolute() + CurPage);

    if ( true === this.Is_Inline() && 0 === CurPage )
    {
        this.AnchorPosition.CalcY = this.Y;
        this.AnchorPosition.Set_Y(this.Pages[CurPage].Height, this.Y, LD_PageFields.Y, LD_PageFields.YLimit, LD_PageLimits.YLimit, PageLimits.Y, PageLimits.YLimit, PageLimits.Y, PageLimits.YLimit);
    }
    else if ( true != this.Is_Inline() && ( 0 === CurPage || ( 1 === CurPage && false === this.RowsInfo[0].FirstPage ) ) )
    {
        this.AnchorPosition.Set_Y(this.Pages[CurPage].Height, this.Pages[CurPage].Y, PageFields.Y, PageFields.YLimit, LD_PageLimits.YLimit, PageLimits.Y, PageLimits.YLimit, PageLimits.Y, PageLimits.YLimit);

        var OtherFlowTables = !this.bPresentation ? editor.WordControl.m_oLogicDocument.DrawingObjects.getAllFloatTablesOnPage( this.Get_StartPage_Absolute() ) : [];
        this.AnchorPosition.Calculate_Y(this.PositionV.RelativeFrom, this.PositionV.Align, this.PositionV.Value);
        this.AnchorPosition.Correct_Values( PageLimits.X, PageLimits.Y, PageLimits.XLimit, PageLimits.YLimit, this.AllowOverlap, OtherFlowTables, this );

        if ( undefined != this.PositionV_Old )
        {
            // Восстанови старые значения, чтобы в историю изменений все нормально записалось
            this.PositionV.RelativeFrom = this.PositionV_Old.RelativeFrom;
            this.PositionV.Align        = this.PositionV_Old.Align;
            this.PositionV.Value        = this.PositionV_Old.Value;

            // Рассчитаем сдвиг с учетом старой привязки
            var Value = this.AnchorPosition.Calculate_Y_Value(this.PositionV_Old.RelativeFrom);
            this.Set_PositionV( this.PositionV_Old.RelativeFrom, false, Value );
            // На всякий случай пересчитаем заново координату
            this.AnchorPosition.Calculate_Y(this.PositionV.RelativeFrom, this.PositionV.Align, this.PositionV.Value);

            this.PositionV_Old = undefined;
        }

        var NewX = this.AnchorPosition.CalcX;
        var NewY = this.AnchorPosition.CalcY;

        this.Shift( CurPage, NewX - this.Pages[CurPage].X, NewY - this.Pages[CurPage].Y );
    }
};
CTable.prototype.private_RecalculateSkipPage = function(CurPage)
{
    this.HeaderInfo.Pages[CurPage] = {};
    this.HeaderInfo.Pages[CurPage].Draw = false;

    for ( var Index = -1; Index < this.Content.length; Index++ )
    {
        if (!this.TableRowsBottom[Index])
            this.TableRowsBottom[Index] = [];

        this.TableRowsBottom[Index][CurPage] = 0;
    }

    this.Pages[CurPage].MaxBotBorder = 0;
    this.Pages[CurPage].BotBorders   = [];

    if (0 === CurPage)
    {
        this.Pages[CurPage].FirstRow = 0;
        this.Pages[CurPage].LastRow  = -1;
    }
    else
    {
        var FirstRow;
        if (true === this.Is_EmptyPage(CurPage - 1))
            FirstRow = this.Pages[CurPage - 1].FirstRow;
        else
            FirstRow = this.Pages[CurPage - 1].LastRow;

        this.Pages[CurPage].FirstRow = FirstRow;
        this.Pages[CurPage].LastRow  = FirstRow -1;
    }
};
CTable.prototype.private_RecalculatePercentWidth = function()
{
    return this.XLimit - this.X - this.Get_TableOffsetCorrection() + this.Get_RightTableOffsetCorrection();
};
//----------------------------------------------------------------------------------------------------------------------
// Класс CTablePage
//----------------------------------------------------------------------------------------------------------------------
function CTablePage(X, Y, XLimit, YLimit, FirstRow, MaxTopBorder)
{
    this.X_origin     = X;
    this.X            = X;
    this.Y            = Y;
    this.XLimit       = XLimit;
    this.YLimit       = YLimit;
    this.Bounds       = new CDocumentBounds(X, Y, XLimit, Y);
    this.MaxTopBorder = MaxTopBorder;
    this.FirstRow     = FirstRow;
    this.LastRow      = FirstRow;
    this.Height       = 0;
    this.LastRowSplit = false;
	this.FootnotesH   = 0;
}
CTablePage.prototype.Shift = function(Dx, Dy)
{
    this.X += Dx;
    this.Y += Dy;
    this.XLimit += Dx;
    this.YLimit += Dy;
    this.Bounds.Shift(Dx, Dy);
};
//----------------------------------------------------------------------------------------------------------------------
// Класс CTableRecalcInfo
//----------------------------------------------------------------------------------------------------------------------
function CTableRecalcInfo()
{
    this.TableGrid     = true;
    this.TableBorders  = true;

    this.CellsToRecalc = {};
    this.CellsAll      = true;
}
CTableRecalcInfo.prototype.Recalc_Borders = function()
{
    this.TableBorders = true;
};
CTableRecalcInfo.prototype.Add_Cell = function(Cell)
{
    this.CellsToRecalc[Cell.Get_Id()] = Cell;
};
CTableRecalcInfo.prototype.Check_Cell = function(Cell)
{
    if ( true === this.CellsAll || undefined != this.CellsToRecalc[Cell.Get_Id()] )
        return true;

    return false;
};
CTableRecalcInfo.prototype.Recalc_AllCells = function()
{
    this.CellsAll = true;
};
CTableRecalcInfo.prototype.Reset = function(bCellsAll)
{
    this.TableGrid     = true;
    this.TableBorders  = true;
    this.CellsAll      = bCellsAll;
    this.CellsToRecalc = {};
};
//----------------------------------------------------------------------------------------------------------------------
// Класс CTableRecalculateObject
//----------------------------------------------------------------------------------------------------------------------
function CTableRecalculateObject()
{
    this.TableSumGrid    = [];
    this.TableGridCalc   = [];

    this.TableRowsBottom = [];
    this.HeaderInfo      = {};
    this.RowsInfo        = [];

    this.X_origin = 0;
    this.X        = 0;
    this.Y        = 0;
    this.XLimit   = 0;
    this.YLimit   = 0;

    this.Pages    = [];

    this.MaxTopBorder = [];
    this.MaxBotBorder = [];
    this.MaxBotMargin = [];

    this.Content = [];
}
CTableRecalculateObject.prototype.Save = function(Table)
{
    this.TableSumGrid    = Table.TableSumGrid;
    this.TableGridCalc   = Table.TableGridCalc;

    this.TableRowsBottom = Table.TableRowsBottom;
    this.HeaderInfo      = Table.HeaderInfo;
    this.RowsInfo        = Table.RowsInfo;

    this.X_origin        = Table.X_origin;
    this.X               = Table.X;
    this.Y               = Table.Y;
    this.XLimit          = Table.XLimit;
    this.YLimit          = Table.YLimit;

    this.Pages           = Table.Pages;

    this.MaxTopBorder    = Table.MaxTopBorder;
    this.MaxBotBorder    = Table.MaxBotBorder;
    this.MaxBotMargin    = Table.MaxBotBorder;

    var Count = Table.Content.length;
    for ( var Index = 0; Index < Count; Index++ )
    {
        this.Content[Index] = Table.Content[Index].SaveRecalculateObject();
    }
};
CTableRecalculateObject.prototype.Load = function(Table)
{
    Table.TableSumGrid    = this.TableSumGrid;
    Table.TableGridCalc   = this.TableGridCalc;

    Table.TableRowsBottom = this.TableRowsBottom;
    Table.HeaderInfo      = this.HeaderInfo;
    Table.RowsInfo        = this.RowsInfo;

    Table.X_origin        = this.X_origin;
    Table.X               = this.X;
    Table.Y               = this.Y;
    Table.XLimit          = this.XLimit;
    Table.YLimit          = this.YLimit;

    Table.Pages           = this.Pages;

    Table.MaxTopBorder    = this.MaxTopBorder;
    Table.MaxBotBorder    = this.MaxBotBorder;
    Table.MaxBotMargin    = this.MaxBotBorder;

    var Count = this.Content.length;
    for ( var Index = 0; Index < Count; Index++ )
    {
        Table.Content[Index].LoadRecalculateObject( this.Content[Index] );
    }
};
CTableRecalculateObject.prototype.Get_DrawingFlowPos = function(FlowPos)
{
    var Count = this.Content.length;
    for (var Index = 0; Index < Count; Index++)
    {
        this.Content[Index].Get_DrawingFlowPos(FlowPos);
    }
};