/* * (c) Copyright Ascensio System SIA 2010-2024 * * 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 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * 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"; var c_oAscSectionBreakType = Asc.c_oAscSectionBreakType; CTable.prototype.Recalculate_Page = function(PageIndex) { //------------------------------------------------------------------------------------------------------------- // Обрабатываем настройку "не отрывать от следующего" //------------------------------------------------------------------------------------------------------------- let result = this.RecalculateKeepNext(PageIndex); if (result !== recalcresult_NextElement) return result; this.SetIsRecalculated(true); 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); result = this.private_RecalculatePage(PageIndex); if (result & recalcresult_CurPage) return result; this.private_RecalculatePositionY(PageIndex); if (result & recalcresult_NextElement) this.RecalcInfo.Reset(false); if (result & recalcresult_NextElement && window['AscCommon'].g_specialPasteHelper && window['AscCommon'].g_specialPasteHelper.showButtonIdParagraph === this.GetId()) window['AscCommon'].g_specialPasteHelper.SpecialPasteButtonById_Show(); this.Sections[this.Sections.length - 1].endPage = PageIndex; 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; this.HeaderInfo.Pages[PageIndex] = {}; this.HeaderInfo.Pages[PageIndex].Draw = false; for (let rowIndex = -1; rowIndex < this.Content.length; ++rowIndex) { if (!this.TableRowsBottom[rowIndex]) this.TableRowsBottom[rowIndex] = []; this.TableRowsBottom[rowIndex][PageIndex] = 0; } } }; 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.Pages[0].LastRow = -1; this.HeaderInfo.Pages[0] = {}; this.HeaderInfo.Pages[0].Draw = false; this.RowsInfo[0] = new CTableRowsInfo(); this.RowsInfo[0].Init(); // Обнуляем таблицу суммарных высот ячеек 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; let logicDocument = this.GetLogicDocument(); if (!this.Parent || !this.Parent.IsAllowSectionBreak() || !logicDocument) return true; let docSections = logicDocument.GetSections(); var isPageBreakOnPrevLine = false; var isColumnBreakOnPrevLine = false; var PrevElement = this.Get_DocumentPrev(); while (PrevElement && (PrevElement instanceof CBlockLevelSdt)) PrevElement = PrevElement.GetLastElement(); if (null !== PrevElement && type_Paragraph === PrevElement.Get_Type() && true === PrevElement.Is_Empty() && undefined !== PrevElement.Get_SectionPr()) { var PrevSectPr = PrevElement.Get_SectionPr(); var CurSectPr = docSections.GetSectPrByElement(this); if (c_oAscSectionBreakType.Continuous === CurSectPr.Get_Type() && true === CurSectPr.Compare_PageSize(PrevSectPr)) 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 = docSections.GetSectPrByElement(this); 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.GetAbsoluteColumn(CurPage) || (0 === CurPage && null !== PrevElement))) || (true === isColumnBreakOnPrevLine && 0 === CurPage)) { this.Recalculate_SkipPage(CurPage); return false; } return true; }; CTable.prototype.private_RecalculateGrid = function(pageFields) { if (this.GetRowsCount() <= 0) return; //--------------------------------------------------------------------------- // 1 часть пересчета ширины таблицы : Рассчитываем фиксированную ширину //--------------------------------------------------------------------------- var TablePr = this.Get_CompiledPr(false).TablePr; var PctWidth = this.CalculatedPctWidth; var MinWidth = this.CalculatedMinWidth; var nTableW = this.CalculatedTableW; if (null === this.CalculatedX || null === this.CalculatedXLimit || Math.abs(this.X - this.CalculatedX) > 0.001 || Math.abs(this.XLimit - this.CalculatedXLimit)) { this.CalculatedX = this.X; this.CalculatedXLimit = this.XLimit; this.RecalcInfo.TableGrid = true; this.RecalcInfo.TableBorders = true; // Запускаем пересчет границ, потому что там пересчитываются метрики ячеек, которые зависят от X, XLimit } if (this.RecalcInfo.TableGrid) { let layoutCoeff = this.getLayoutScaleCoefficient(); var arrSumGrid = []; var nTempSum = 0; arrSumGrid[-1] = 0; for (var nIndex = 0, nCount = this.TableGrid.length; nIndex < nCount; ++nIndex) { nTempSum += this.TableGrid[nIndex] * layoutCoeff; arrSumGrid[nIndex] = nTempSum; } PctWidth = this.private_RecalculatePercentWidth(); MinWidth = this.private_GetTableMinWidth() * layoutCoeff; nTableW = 0; if (tblwidth_Auto === TablePr.TableW.Type) { nTableW = 0; } else if (tblwidth_Nil === TablePr.TableW.Type) { nTableW = MinWidth; } else { if (tblwidth_Pct === TablePr.TableW.Type) nTableW = PctWidth * TablePr.TableW.W / 100; else nTableW = TablePr.TableW.W * layoutCoeff; if (0.001 > nTableW) nTableW = 0; else if (nTableW < MinWidth) nTableW = MinWidth; } var nCurGridCol = 0; for (var nCurRow = 0, nRowsCount = this.GetRowsCount(); nCurRow < nRowsCount; ++nCurRow) { var oRow = this.GetRow(nCurRow); oRow.SetIndex(nCurRow); // Смотрим на ширину пропущенных колонок сетки в начале строки var oBeforeInfo = oRow.GetBefore(); nCurGridCol = oBeforeInfo.Grid; if (nCurGridCol > 0 && arrSumGrid[nCurGridCol - 1] < oBeforeInfo.W) { var nTempDiff = oBeforeInfo.W - arrSumGrid[nCurGridCol - 1]; for (var nTempIndex = nCurGridCol - 1; nTempIndex < arrSumGrid.length; ++nTempIndex) { arrSumGrid[nTempIndex] += nTempDiff; } } var nCellSpacing = oRow.GetCellSpacing(); for (var nCurCell = 0, nCellsCount = oRow.GetCellsCount(); nCurCell < nCellsCount; ++nCurCell) { var oCell = oRow.GetCell(nCurCell); oCell.SetIndex(nCurCell); var oCellW = oCell.GetW(); var nGridSpan = oCell.GetGridSpan(); if (vmerge_Restart !== oCell.GetVMerge()) { nCurGridCol += nGridSpan; continue; } if (nCurGridCol + nGridSpan - 1 > arrSumGrid.length) { for (var nAddIndex = arrSumGrid.length; nAddIndex <= nCurGridCol + nGridSpan - 1; ++nAddIndex) { // Добавляем столбик шириной в 2 см arrSumGrid[nAddIndex] = arrSumGrid[nAddIndex - 1] + 20; } } if (tblwidth_Auto !== oCellW.Type && tblwidth_Nil !== oCellW.Type) { var nCellWidth = 0; if (tblwidth_Pct === oCellW.Type) nCellWidth = PctWidth * oCellW.W / 100; else nCellWidth = oCellW.W * layoutCoeff; if (null !== nCellSpacing) { if (0 === nCurCell) nCellWidth += nCellSpacing / 2; nCellWidth += nCellSpacing; if (nCellsCount - 1 === nCurCell) nCellWidth += nCellSpacing / 2; } if (nCellWidth + arrSumGrid[nCurGridCol - 1] > arrSumGrid[nCurGridCol + nGridSpan - 1]) { var nTempDiff = nCellWidth + arrSumGrid[nCurGridCol - 1] - arrSumGrid[nCurGridCol + nGridSpan - 1]; for (var nTempIndex = nCurGridCol + nGridSpan - 1; nTempIndex < arrSumGrid.length; ++nTempIndex) { arrSumGrid[nTempIndex] += nTempDiff; } } } nCurGridCol += nGridSpan; } // Смотрим на ширину пропущенных колонок сетки в конце строки var oAfterInfo = oRow.GetAfter(); if (nCurGridCol + oAfterInfo.Grid - 1 > arrSumGrid.length) { for (var nAddIndex = arrSumGrid.length; nAddIndex <= nCurGridCol + oAfterInfo.Grid - 1; ++nAddIndex) { // Добавляем столбик шириной в 2 см arrSumGrid[nAddIndex] = arrSumGrid[nAddIndex - 1] + 20; } } if (arrSumGrid[nCurGridCol + oAfterInfo.Grid - 1] < oAfterInfo.W + arrSumGrid[nCurGridCol - 1]) { var nTempDiff = oAfterInfo.W + arrSumGrid[nCurGridCol - 1] - arrSumGrid[nCurGridCol + oAfterInfo.Grid - 1]; for (var nTempIndex = nCurGridCol + oAfterInfo.Grid - 1; nTempIndex < arrSumGrid.length; ++nTempIndex) { arrSumGrid[nTempIndex] += nTempDiff; } } } // TODO: разобраться с минимальной шириной таблицы и ячеек // Задана общая ширина таблицы и последняя ячейка вышла за пределы // данной ширины. Уменьшаем все столбцы сетки пропорционально, чтобы // суммарная ширина стала равной заданной ширине таблицы. if (nTableW > 0 && Math.abs(arrSumGrid[arrSumGrid.length - 1] - nTableW) > 0.01) arrSumGrid = this.Internal_ScaleTableWidth(arrSumGrid, nTableW); else if (MinWidth > arrSumGrid[arrSumGrid.length - 1]) arrSumGrid = this.Internal_ScaleTableWidth(arrSumGrid, arrSumGrid[arrSumGrid.length - 1]); // По массиву SumGrid восстанавливаем ширины самих колонок this.TableGridCalc = []; this.TableGridCalc[0] = arrSumGrid[0]; for (var nIndex = 1, nCount = arrSumGrid.length; nIndex < nCount; ++nIndex) { this.TableGridCalc[nIndex] = arrSumGrid[nIndex] - arrSumGrid[nIndex - 1]; } this.TableSumGrid = arrSumGrid; this.CalculatedPctWidth = PctWidth; this.CalculatedMinWidth = MinWidth; this.CalculatedTableW = nTableW; this.RecalcInfo.TableGrid = false; } var TopTable = this.Parent ? this.Parent.IsInTable(true) : null; if ((null === TopTable && tbllayout_AutoFit === TablePr.TableLayout) || (null != TopTable && tbllayout_AutoFit === TopTable.Get_CompiledPr(false).TablePr.TableLayout)) { //--------------------------------------------------------------------------- // 2 часть пересчета ширины таблицы : Рассчитываем ширину по содержимому //--------------------------------------------------------------------------- // 1. Расчитаем минимальные и максимальные значения ширины для всех колонок, а также минимальное значение // отступов и предпочитаемую ширину колонок var arrMinMargin = [], arrMinContent = [], arrMaxContent = [], arrPreferred = [], // 0 - ориентируемся на содержимое ячеек, > 0 - ориентируемся только на ширину ячеек записанную в свойствах arrMinNoPref = []; // минимальное значение контента, только без учета предпочитаемы ширин var nColsCount = this.TableGridCalc.length; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrMinMargin[nCurCol] = 0; arrMinContent[nCurCol] = 0; arrMaxContent[nCurCol] = 0; arrPreferred[nCurCol] = 0; arrMinNoPref[nCurCol] = 0; } this.private_RecalculateGridMinContent(PctWidth, arrMinMargin, arrMinContent, arrMaxContent, arrPreferred, arrMinNoPref); // 2. Проследим, чтобы значения arrMinContent и arrMaxContent не превосходили значение 55,87см(так работает Word) for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { if (arrMaxContent[nCurCol] < arrMinContent[nCurCol]) arrMaxContent[nCurCol] = arrMinContent[nCurCol]; if (arrMinNoPref[nCurCol] > 558.7) arrMinNoPref[nCurCol] = 558.7; if (arrMinContent[nCurCol] > 558.7) arrMinContent[nCurCol] = 558.7; if (arrMaxContent[nCurCol] > 558.7) arrMaxContent[nCurCol] = 558.7; } // 3. Рассчитаем максимально допустимую ширину под всю таблицу var oPageFields; if (pageFields) { oPageFields = { X : pageFields.X, Y : pageFields.Y, XLimit : pageFields.XLimit, YLimit : pageFields.YLimit }; } else if (!this.Parent) { oPageFields = { X : 0, Y : 0, XLimit : 0, YLimit : 0 }; } else { // Случай, когда таблица лежит внутри CBlockLevelSdt if (this.Parent instanceof CDocumentContent && this.LogicDocument && this.Parent.IsBlockLevelSdtContent() && this.Parent.GetTopDocumentContent() === this.LogicDocument && !this.Parent.IsTableCellContent()) oPageFields = this.LogicDocument.GetColumnFields(this.GetAbsolutePage(this.PageNum), this.GetAbsoluteColumn(this.PageNum), this.GetAbsoluteSection(this.PageNum)); if (!oPageFields) oPageFields = this.Parent.GetColumnFields ? this.Parent.GetColumnFields(this.GetAbsolutePage(this.PageNum), this.GetAbsoluteColumn(this.PageNum), this.GetAbsoluteSection(this.PageNum)) : this.Parent.Get_PageFields(this.GetRelativePage(this.PageNum), this.Parent.IsHdrFtr()); } this.CalculatedPageFields = { X : oPageFields.X, Y : oPageFields.Y, XLimit : oPageFields.XLimit, YLimit : oPageFields.YLimit }; var oFramePr = this.GetFramePr(); if (oFramePr && undefined !== oFramePr.GetW()) { oPageFields.X = 0; oPageFields.XLimit = oFramePr.GetW(); } else if (this.LogicDocument && this.LogicDocument.IsDocumentEditor() && this.IsInline()) { var _X = oPageFields.X; var _XLimit = oPageFields.XLimit; var arrRanges = this.Parent.CheckRange(_X, this.Y, _XLimit, this.Y + 0.001, this.Y, this.Y + 0.001, _X, _XLimit, this.GetRelativePage(0)); if (arrRanges.length > 0) { for (var nRangeIndex = 0, nRangesCount = arrRanges.length; nRangeIndex < nRangesCount; ++nRangeIndex) { if (arrRanges[nRangeIndex].X0 < oPageFields.X + 3.2 && arrRanges[nRangeIndex].X1 > _X) _X = arrRanges[nRangeIndex].X1 + 0.001; if (arrRanges[nRangeIndex].X1 > oPageFields.XLimit - 3.2 && arrRanges[nRangeIndex].X0 < _XLimit) _XLimit = arrRanges[nRangeIndex].X0 - 0.001; } } oPageFields.X = _X; oPageFields.XLimit = _XLimit; } var nMaxTableW = oPageFields.XLimit - oPageFields.X - TablePr.TableInd - this.GetTableOffsetCorrection() + this.GetRightTableOffsetCorrection(); // 4. Рассчитаем желаемую ширину таблицы таблицы var arrMaxOverMin = [], nSumMaxOverMin = 0, nSumMin = 0, nSumMinMargin = 0, nSumMax = 0; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrMaxOverMin[nCurCol] = Math.max(0, arrMaxContent[nCurCol] - arrMinContent[nCurCol]); nSumMin += arrMinContent[nCurCol]; nSumMaxOverMin += arrMaxOverMin[nCurCol]; nSumMinMargin += arrMinMargin[nCurCol]; nSumMax += arrMinContent[nCurCol] + arrMaxOverMin[nCurCol]; } var nMaxTableWOrigin = nMaxTableW; if ((TablePr.TableW.IsMM() || TablePr.TableW.IsPercent()) && nMaxTableW < nTableW) nMaxTableW = nTableW; if (nSumMin < nMaxTableW) { if (nSumMax <= nMaxTableW || nSumMaxOverMin < 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = Math.max(arrMinContent[nCurCol], arrMaxContent[nCurCol]); } } else { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinContent[nCurCol] + (nMaxTableW - nSumMin) * arrMaxOverMin[nCurCol] / nSumMaxOverMin; } } // Если у таблицы задана ширина, тогда ориентируемся по ширине, а если нет, тогда ориентируемся по // максимальным значениям. if (TablePr.TableW.IsMM() || TablePr.TableW.IsPercent()) { if (nSumMin < 0.001 && nSumMax < 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = nTableW / nColsCount; } } else if (nSumMin >= nTableW) { var nSumMinContent = 0, nSumPrefOverMinContent = 0, arrPrefOverMinContent = []; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrPrefOverMinContent[nCurCol] = Math.max(0, arrMinContent[nCurCol] - arrMinNoPref[nCurCol]); nSumPrefOverMinContent += arrPrefOverMinContent[nCurCol]; nSumMinContent += arrMinNoPref[nCurCol]; } if (nSumMinContent >= nTableW) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol]; } } else { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol] + arrPrefOverMinContent[nCurCol] * (nTableW - nSumMinContent) / nSumPrefOverMinContent; } } } else { // nSumMin < nMaxTableW, значит у нас есть свободное пространство для распределения // Колонки, у которых задана предпочитаемая ширина не трогаем, с одним исключением, когда // такие колонки все, в этом случае растягиваем их пропрорционально var nSumMaxOverMin = 0, isAllPreferred = true, arrNoPrefMaxOverMin = [], nSumMaxOverMin = 0, nSumNonPrefMax = 0, nSumPrefMin = 0, nSumNoPrefMin = 0; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { if (arrPreferred[nCurCol] < 0.001) { isAllPreferred = false; var nMinW = arrMinContent[nCurCol]; var nMaxW = Math.max(arrMaxContent[nCurCol], nMinW); arrNoPrefMaxOverMin[nCurCol] = nMaxW - nMinW; nSumMaxOverMin += nMaxW - nMinW; nSumNonPrefMax += nMaxW; nSumNoPrefMin += nMinW; } else { nSumPrefMin += arrMinContent[nCurCol]; arrNoPrefMaxOverMin[nCurCol] = 0; } } // Если данное условие выполняется, значит у нас все ячейки с предпочитаемыми значениями, тогда // мы растягиваем все ячейки равномерно. Если не выполняется, значит есть ячейки, в которых // предпочитаемое значение не задано, и тогда растягиваем только такие ячейки. if (isAllPreferred) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinContent[nCurCol] * nTableW / nSumMin; } } else if (nSumNonPrefMax < nMaxTableW - nSumMin && nSumNonPrefMax > 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { if (arrPreferred[nCurCol] < 0.001) this.TableGridCalc[nCurCol] = arrMaxContent[nCurCol] * (nTableW - nSumPrefMin) / nSumNonPrefMax; else this.TableGridCalc[nCurCol] = arrMinContent[nCurCol]; } } else if (nSumMaxOverMin > 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinContent[nCurCol] + (nTableW - nSumMin) * arrNoPrefMaxOverMin[nCurCol] / nSumMaxOverMin; } } else if (nSumNoPrefMin > 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { if (arrPreferred[nCurCol] < 0.001) this.TableGridCalc[nCurCol] = arrMinContent[nCurCol] * (nTableW - nSumPrefMin) / nSumNoPrefMin; else this.TableGridCalc[nCurCol] = arrMinContent[nCurCol]; } } else { // Такого быть не должно } } } } else { // Если в таблице сделать все ячейки нулевой ширины (для контента), и все равно она получается шире // максимальной допустимой ширины, тогда выставляем ширины всех колоно по минимальному значению // маргинов и оставляем так как есть if (nMaxTableW - nSumMinMargin < 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinMargin[nCurCol]; } } else { var nSumMinContent = 0, nSumPrefOverMinContent = 0, arrPrefOverMinContent = []; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrPrefOverMinContent[nCurCol] = Math.max(0, arrMinContent[nCurCol] - arrMinNoPref[nCurCol]); nSumPrefOverMinContent += arrPrefOverMinContent[nCurCol]; nSumMinContent += arrMinNoPref[nCurCol]; } if (nSumMinContent > nMaxTableW) { if (nTableW > nMaxTableWOrigin) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol]; } } else { var nSumMinNoPrefOverMinMargin = 0, arrMinNoPrefOverMinMargin = []; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrMinNoPrefOverMinMargin[nCurCol] = Math.max(arrMinNoPref[nCurCol] - arrMinMargin[nCurCol], 0); nSumMinNoPrefOverMinMargin += arrMinNoPrefOverMinMargin[nCurCol]; } if (nSumMinNoPrefOverMinMargin > 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinMargin[nCurCol] + arrMinNoPrefOverMinMargin[nCurCol] * (nMaxTableW - nSumMinMargin) / nSumMinNoPrefOverMinMargin; } } else { // Такого быть не должно, т.к. мы в ветке nMaxTableW > nSumMinMargin + 0.001 && nSumMinContent > nMaxTableW } } } else { // Если попали в эту ветку, то это может означать только одно, что у нас есть ячейки с заданной // шириной, превышающей минимальное значения ширины контента if ((TablePr.TableW.IsMM() || TablePr.TableW.IsPercent()) && nTableW < nSumMinContent) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol]; } } else { var _nTableW = (TablePr.TableW.IsMM() || TablePr.TableW.IsPercent() ? nTableW : nMaxTableWOrigin); var nSumPrefOverMinContent = 0, arrPrefOverMinContent = []; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { arrPrefOverMinContent[nCurCol] = Math.max(arrMinContent[nCurCol] - arrMinNoPref[nCurCol], 0); nSumPrefOverMinContent += arrPrefOverMinContent[nCurCol]; } if (nSumPrefOverMinContent > 0.001) { for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol] + arrPrefOverMinContent[nCurCol] * (_nTableW - nSumMinContent) / nSumPrefOverMinContent; } } else { // Такого быть не должно for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) { this.TableGridCalc[nCurCol] = arrMinNoPref[nCurCol]; } } } } } } this.TableSumGrid[-1] = 0; for (var nCurCol = 0; nCurCol < nColsCount; ++nCurCol) this.TableSumGrid[nCurCol] = this.TableSumGrid[nCurCol - 1] + this.TableGridCalc[nCurCol]; } }; CTable.prototype.private_RecalculateGridMinContent = function(nPctWidth, arrMinMargins, arrMinContent, arrMaxContent, arrPreferred, arrMinNoPreferred) { // Сначала мы высчитываем минимальный и максимальный контент для всех ячеек с GridSpan=1, ячейки // у которых GridSpan > 1 заносим в массив arrMergedColumns. // После обработки всех ячеек с GridSpan=1, мы пробегаемся по массиву arrMergedPreferred, это ячейки, у которых // GridSpan > 1 и у которых задана предпочтительная ширина. Далее на основе этих ячееки высчитываем предпочтительную // ширину колонок. Причем делаем это начиная с ячеек с наименьшим GridSpan. // Далее пробегаемся по arrMergedCells и если какой-нибудь отрезок из колонок (StartGrid, StartGrid+GridSpan) // по ширине занимает место больше, чем эти колонки по отдельности, тогда мы добавочное место распределяем среди // колонок с сохранением соотношения их ширины, как частный случай, если у нас встретилась колонка нулевой ширины, // например, у которой вообще не было целой ячейки внутри колонки, тогда она так и останется нулевой ширины // после распределения. Причем тут важно, что мы пробегаемся по массиву подряд, а не на основе GridSpan, как // у arrMergedPreferred, поэтому результат для одной и той же таблицы, с переставленными двумя строками может // быть разным. let layoutCoeff = this.getLayoutScaleCoefficient(); var arrMergedColumns = []; var arrMergedPreferred = []; var nMergedPrefCount = 0; for (var nCurRow = 0, nRowsCount = this.GetRowsCount(); nCurRow < nRowsCount; ++nCurRow) { var oRow = this.GetRow(nCurRow); var nSpacing = oRow.GetCellSpacing(); var nSpacingW = (null !== nSpacing ? nSpacing : 0); var nCurGridCol = 0; var oBeforeInfo = oRow.GetBefore(); var nGridBefore = oBeforeInfo.Grid; var nBeforeW = oBeforeInfo.W.GetCalculatedValue(nPctWidth); if (nBeforeW > 0 && nGridBefore > 0) { if (1 === nGridBefore) { if (arrMinContent[nCurGridCol] < nBeforeW) arrMinContent[nCurGridCol] = nBeforeW; if (0 === arrPreferred[nCurGridCol] || arrPreferred[nCurGridCol] < nBeforeW) arrPreferred[nCurGridCol] = nBeforeW; if (arrPreferred[nCurGridCol]) arrMaxContent[nCurGridCol] = arrPreferred[nCurGridCol]; else if (arrMaxContent[nCurGridCol] < nBeforeW) arrMaxContent[nCurGridCol] = nBeforeW; } else { arrMergedColumns.push({ Start : nCurGridCol, Min : nBeforeW, Max : nBeforeW, Preferred : nBeforeW, Margins : 0, Grid : nGridBefore, MinNoPref : 0 }); if (!arrMergedPreferred[nGridBefore]) arrMergedPreferred[nGridBefore] = []; arrMergedPreferred[nGridBefore].push({ Start : nCurGridCol, W : nBeforeW }); nMergedPrefCount++; } } nCurGridCol = nGridBefore; for (var nCurCell = 0, nCellsCount = oRow.GetCellsCount(); nCurCell < nCellsCount; ++nCurCell) { var oCell = oRow.GetCell(nCurCell); var oCellMinMax = oCell.RecalculateMinMaxContentWidth(false, nPctWidth); var nGridSpan = oCell.GetGridSpan(); var nCellMin = oCellMinMax.Min; var nCellMax = oCellMinMax.Max; var oCellW = oCell.GetW(); var oMargins = oCell.GetMargins(); var nPreferred = oCellW.GetCalculatedValue(nPctWidth) * layoutCoeff; var nSpacingAdd = (0 === nCurCell || nCellsCount - 1 === nCurCell) ? 3 / 2 * nSpacingW : nSpacingW; var nMarginsMin = nSpacingAdd + oMargins.Left.W + oMargins.Right.W; nCellMin += nSpacingAdd; nCellMax += nSpacingAdd; if (1 === nGridSpan) { if (arrMinContent[nCurGridCol] < nCellMin) arrMinContent[nCurGridCol] = nCellMin; if (nPreferred && arrPreferred[nCurGridCol] < nPreferred) arrPreferred[nCurGridCol] = nPreferred; if (arrPreferred[nCurGridCol]) arrMaxContent[nCurGridCol] = arrPreferred[nCurGridCol]; else if (arrMaxContent[nCurGridCol] < nCellMax) arrMaxContent[nCurGridCol] = nCellMax; if (arrMinMargins[nCurGridCol] < nMarginsMin) arrMinMargins[nCurGridCol] = nMarginsMin; if (arrMinNoPreferred[nCurGridCol] < oCellMinMax.ContentMin + nSpacingAdd) arrMinNoPreferred[nCurGridCol] = oCellMinMax.ContentMin + nSpacingAdd; } else if (nGridSpan > 1) { arrMergedColumns.push({ Start : nCurGridCol, Min : nCellMin, Max : nCellMax, Preferred : nPreferred, Margins : nMarginsMin, Grid : nGridSpan, MinNoPref : oCellMinMax.ContentMin + nSpacingAdd }); if (nPreferred > 0.001) { if (!arrMergedPreferred[nGridSpan]) arrMergedPreferred[nGridSpan] = []; arrMergedPreferred[nGridSpan].push({ Start : nCurGridCol, W : nPreferred }); nMergedPrefCount++; } } nCurGridCol += nGridSpan; } var oAfterInfo = oRow.GetAfter(); var nGridAfter = oAfterInfo.Grid; var nAfterW = oAfterInfo.W.GetCalculatedValue(nPctWidth); if (nAfterW > 0 && nGridAfter > 0) { if (1 === nGridAfter) { if (arrMinContent[nCurGridCol] < nAfterW) arrMinContent[nCurGridCol] = nAfterW; if (0 === arrPreferred[nCurGridCol] || arrPreferred[nCurGridCol] < nAfterW) arrPreferred[nCurGridCol] = nAfterW; if (arrPreferred[nCurGridCol]) arrMaxContent[nCurGridCol] = arrPreferred[nCurGridCol]; else if (arrMaxContent[nCurGridCol] < nAfterW) arrMaxContent[nCurGridCol] = nAfterW; } else { arrMergedColumns.push({ Start : nCurGridCol, Min : nAfterW, Max : nAfterW, Preferred : nAfterW, Margins : 0, Grid : nGridAfter, MinNoPref : 0 }); if (!arrMergedPreferred[nGridAfter]) arrMergedPreferred[nGridAfter] = []; arrMergedPreferred[nGridAfter].push({ Start : nCurGridCol, W : nAfterW }); nMergedPrefCount++; } } } while (nMergedPrefCount > 0) { var nPrevCount = nMergedPrefCount; for (var nGridSpan = 2, nMaxGridSpan = arrMergedPreferred.length; nGridSpan < nMaxGridSpan; ++nGridSpan) { if (!arrMergedPreferred[nGridSpan]) continue; for (var nIndex = arrMergedPreferred[nGridSpan].length - 1; nIndex >= 0; --nIndex) { var nStart = arrMergedPreferred[nGridSpan][nIndex].Start; var nPreferred = arrMergedPreferred[nGridSpan][nIndex].W; var nPreferredSum = 0; for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan - 1; ++nCurSpan) { if (arrPreferred[nCurSpan] > 0 && -1 !== nPreferredSum) nPreferredSum += arrPreferred[nCurSpan]; else nPreferredSum = -1; } if (-1 !== nPreferredSum && nPreferred > nPreferredSum && arrPreferred[nStart + nGridSpan - 1] < nPreferred - nPreferredSum) { arrPreferred[nStart + nGridSpan - 1] = nPreferred - nPreferredSum; if (arrMinContent[nStart + nGridSpan - 1] < arrPreferred[nStart + nGridSpan - 1]) arrMinContent[nStart + nGridSpan - 1] = arrPreferred[nStart + nGridSpan - 1]; arrMergedPreferred[nGridSpan].splice(nIndex, 1); nMergedPrefCount--; } } } if (nPrevCount <= nMergedPrefCount) break; } for (var nIndex = 0, nCount = arrMergedColumns.length; nIndex < nCount; ++nIndex) { var nStart = arrMergedColumns[nIndex].Start; var nMin = arrMergedColumns[nIndex].Min; var nMax = arrMergedColumns[nIndex].Max; var nMargins = arrMergedColumns[nIndex].Margins; var nPreferred = arrMergedColumns[nIndex].Preferred; var nGridSpan = arrMergedColumns[nIndex].Grid; var nMinNoPref = arrMergedColumns[nIndex].MinNoPref; var nSumSpanMin = 0; var nSumSpanMax = 0; var nSumMargin = 0; var nSumSpanMinNoPref = 0; for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { nSumSpanMin += arrMinContent[nCurSpan]; nSumSpanMax += arrMaxContent[nCurSpan]; nSumMargin += arrMinMargins[nCurSpan]; nSumSpanMinNoPref += arrMinNoPreferred[nCurSpan]; } if (nMargins > nSumMargin) { if (nSumMargin < 0.001) { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinMargins[nCurSpan] = nMargins / nGridSpan; } } else { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinMargins[nCurSpan] = arrMinMargins[nCurSpan] * nMargins / nSumMargin; } } } if (nMin > nSumSpanMin) { if (nSumSpanMin < 0.001) { // Такого не должно быть, но на всякий случай в данной ситуации делаем равномерное распределение for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinContent[nCurSpan] = nMin / nGridSpan; } } else { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinContent[nCurSpan] = arrMinContent[nCurSpan] * nMin / nSumSpanMin; } } } if (nMinNoPref > nSumSpanMinNoPref) { if (nSumSpanMin < 0.001) { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinNoPreferred[nCurSpan] = nMinNoPref / nGridSpan; } } else { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { arrMinNoPreferred[nCurSpan] = arrMinContent[nCurSpan] * nMinNoPref / nSumSpanMin; } } } if (nMax > nSumSpanMax) { // Если у нас в объединении несколько колонок, тогда явно записанная ширина ячейки не // перекрывает ширину ни одной из колонок, она всего лишь учавствует в определении // максимальной ширины. // Высчитываем сумму максимумов по колонкам, у которых стоит флаг false var nSumSpanMax2 = 0; for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { if (arrPreferred[nCurSpan] < 0.001) nSumSpanMax2 += arrMaxContent[nCurSpan]; } // Если nSumSpanMax2=0, тогда во всех колонках выставлен флаг=true и мы должны учитывать // уже имеющиеся там значения, а текущее значение будет проигнорировано if (nSumSpanMax2 > 0.001) { for (var nCurSpan = nStart; nCurSpan < nStart + nGridSpan; ++nCurSpan) { if (arrPreferred[nCurSpan] < 0.001) { arrMaxContent[nCurSpan] = arrMaxContent[nCurSpan] * (nSumSpanMax - nMax + nSumSpanMax2) / nSumSpanMax2; if (nPreferred > 0.001) arrPreferred[nCurSpan] = arrMaxContent[nCurSpan]; } } } } } }; 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(); var nRowsCountInHeader = this.GetRowsCountInHeader(); var oHeaderLastRow = nRowsCountInHeader ? this.GetRow(nRowsCountInHeader - 1) : null; 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.GetVMerge(); Row.Set_CellInfo( CurCell, CurGridCol, 0, 0, 0, 0, 0, 0 ); // Bug 32418 ячейки, участвующие в вертикальном объединении, все равно участвуют в определении границы // строки, поэтому здесь мы их не пропускаем. var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan ); var CellMargins; var CellBorders; if(!this.bPresentation) { CellMargins = Cell.GetMargins(); CellBorders = Cell.Get_Borders(); if ( CellMargins.Bottom.W > MaxBotMargin[CurRow + VMergeCount - 1] ) MaxBotMargin[CurRow + VMergeCount - 1] = CellMargins.Bottom.W; } else { let oTopCell = this.Internal_Get_StartMergedCell(CurRow, CurGridCol, GridSpan); CellMargins = oTopCell.GetMargins(); CellBorders = oTopCell.Get_Borders(); MaxBotMargin[CurRow] = Math.max(MaxBotMargin[CurRow], CellMargins.Bottom.W); } if ( true === bSpacing_Top ) { if ( border_Single === CellBorders.Top.Value && MaxTopBorder[CurRow] < CellBorders.Top.Size ) MaxTopBorder[CurRow] = CellBorders.Top.Size; Cell.SetBorderInfoTop([CellBorders.Top]); Cell.SetBorderInfoTopHeader([CellBorders.Top]); } else { if ( 0 === CurRow ) { // Сравним границы var Result_Border = this.private_ResolveBordersConflict( 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.SetBorderInfoTop(BorderInfo_Top); Cell.SetBorderInfoTopHeader(BorderInfo_Top); } else { var oCellTopInfo = this.private_RecalculateCellTopBorder(this.GetRow(CurRow - 1), CurRow, CurGridCol, GridSpan, TableBorders, CellBorders); var oCellTopHeaderInfo = oHeaderLastRow ? this.private_RecalculateCellTopBorder(oHeaderLastRow, CurRow, CurGridCol, GridSpan, TableBorders, CellBorders) : oCellTopInfo; if (MaxTopBorder[CurRow] < oCellTopInfo.Max) MaxTopBorder[CurRow] = oCellTopInfo.Max; Cell.SetBorderInfoTop(oCellTopInfo.Info); Cell.SetBorderInfoTopHeader(oCellTopHeaderInfo.Info); } } var CellBordersBottom = CellBorders.Bottom; if (VMergeCount > 1 && !this.bPresentation) { // Берем нижнюю границу нижней ячейки вертикального объединения. 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.private_ResolveBordersConflict( 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.private_ResolveBordersConflict( 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.private_ResolveBordersConflict( 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; let layoutCoeff = this.getLayoutScaleCoefficient(); // Также для каждой ячейки обсчитаем ее метрики и левую и правую границы 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.GetVMerge(); // начальная и конечная точки данного 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.GetMargins(); 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 * layoutCoeff; X_content_end -= CellMar.Right.W * layoutCoeff; if ( border_Single === CellBorders.Left.Value ) X_content_start += CellBorders.Left.Size * layoutCoeff; if ( border_Single === CellBorders.Right.Value ) X_content_end -= CellBorders.Right.Size * layoutCoeff; } else { if ( vmerge_Continue === Vmerge ) { X_content_start += CellMar.Left.W * layoutCoeff; X_content_end -= CellMar.Right.W * layoutCoeff; // Границы для этих ячеек рассчитываются во время расчета границ первой ячейки в вертикальном // объединении. Но может случиться, что если что-то пошло не так и у нас первая же ячейка первой строки // идет с флагом vmerge_Continue. Защищаемся от этого плохого случая if (!Cell.GetBorderInfoLeft()) { let leftBorder = Cell.GetBorders().Left; Cell.Set_BorderInfo_Left([leftBorder], leftBorder.GetWidth()); } if (!Cell.GetBorderInfoRight()) { let rightBorder = Cell.GetBorders().Right; Cell.Set_BorderInfo_Right([rightBorder], rightBorder.GetWidth()); } } else { // Линии правой и левой границы рисуются ровно по сетке // (середина линии(всмысле толщины линии) совпадает с линией сетки). // Мы должны найти максимальную толщину линии, участвущую в правой/левой // границах. Если данная толщина меньше соответствующего отступа, тогда // она не влияет на расположение содержимого ячейки, в противном случае, // максимальная толщина линии и задает отступ для содержимого. // Поэтому первым шагом определим максимальную толщину правой и левой границ. var Max_r_w = 0; var Max_l_w = 0; var Borders_Info = { Right : [], Left : [], Right_Max : 0, Left_Max : 0 }; const oTopMergedCell = this.bPresentation ? this.Internal_Get_StartMergedCell(CurRow, CurGridCol, GridSpan) : null; let oTempCellBorders = oTopMergedCell ? oTopMergedCell.GetBorders() : null; for (var nTempCurRow = 0; nTempCurRow < VMergeCount; ++nTempCurRow) { var oTempRow = this.GetRow(CurRow + nTempCurRow); var nTempCurCell = this.private_GetCellIndexByStartGridCol(CurRow + nTempCurRow, CurGridCol); if (nTempCurCell < 0) continue; const oTempCell = oTempRow.GetCell(nTempCurCell); if (!oTopMergedCell) { oTempCellBorders = oTempCell.GetBorders(); } // Обработка левой границы if (0 === nTempCurCell) { var oLeftBorder = this.private_ResolveBordersConflict(TableBorders.Left, oTempCellBorders.Left, true, false); if (border_Single === oLeftBorder.Value && oLeftBorder.Size > Max_l_w) Max_l_w = oLeftBorder.Size; Borders_Info.Left.push(oLeftBorder); } else { let oLeftBorder; const oLeftCell = oTempRow.GetCell(nTempCurCell - 1); if (this.bPresentation) { const oLeftTopCell = this.GetStartMergedCell(nTempCurCell - 1, CurRow + nTempCurRow); oLeftBorder = this.private_ResolveBordersConflict(oLeftTopCell.GetBorders().Right, oTempCellBorders.Left, false, false, oLeftCell === oLeftTopCell, oTempCell === oTopMergedCell); } else { oLeftBorder = this.private_ResolveBordersConflict(oLeftCell.GetBorders().Right, oTempCellBorders.Left, false, false); } if (border_Single === oLeftBorder.Value && oLeftBorder.Size > Max_l_w) Max_l_w = oLeftBorder.Size; Borders_Info.Left.push(oLeftBorder); } // Обработка правой границы if (oTempRow.GetCellsCount() - 1 === nTempCurCell) { var oRightBorder = this.private_ResolveBordersConflict(TableBorders.Right, oTempCellBorders.Right, true, false); if (border_Single === oRightBorder.Value && oRightBorder.Size > Max_r_w) Max_r_w = oRightBorder.Size; Borders_Info.Right.push(oRightBorder); } else { let oRightBorder; const oRightCell = oTempRow.GetCell(nTempCurCell + 1); if (this.bPresentation) { const oRightTopCell = this.GetStartMergedCell(nTempCurCell + 1, CurRow + nTempCurRow); oRightBorder = this.private_ResolveBordersConflict(oRightTopCell.GetBorders().Left, oTempCellBorders.Right,false, false, oRightCell === oRightTopCell, oTopMergedCell === oTempCell); } else { oRightBorder = this.private_ResolveBordersConflict(oRightCell.GetBorders().Left, oTempCellBorders.Right, false, false); } if (border_Single === oRightBorder.Value && oRightBorder.Size > Max_r_w) Max_r_w = oRightBorder.Size; Borders_Info.Right.push(oRightBorder); } } 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 * layoutCoeff; else X_content_start += CellMar.Left.W * layoutCoeff; if ( Max_r_w / 2 > CellMar.Right.W ) X_content_end -= Max_r_w / 2 * layoutCoeff; else X_content_end -= CellMar.Right.W * layoutCoeff; 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.GetBorderInfo(); 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.GetBorderInfo(); 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_RecalculateCellTopBorder = function(oPrevRow, nCurRow, nCurGridCol, nGridSpan, oTableBorders, oCellBorders) { // Ищем в предыдущей строке первую ячейку, пересекающуюся с [nCurGridCol, nCurGridCol + nGridSpan] var nPrevCellsCount = oPrevRow.GetCellsCount(); var oPrevBefore = oPrevRow.GetBefore(); var oPrevAfter = oPrevRow.GetAfter(); var nPrevPos = -1; var nPrevGridCol = oPrevBefore.Grid; for (var nPrevCell = 0; nPrevCell < nPrevCellsCount; ++nPrevCell) { var oPrevCell = oPrevRow.GetCell(nPrevCell); var nPrevGridSpan = oPrevCell.GetGridSpan(); if (nPrevGridCol <= nCurGridCol + nGridSpan - 1 && nPrevGridCol + nPrevGridSpan - 1 >= nCurGridCol) { nPrevPos = nPrevCell; break; } nPrevGridCol += nPrevGridSpan; } var arrBorderTopInfo = []; var nMaxTopBorder = 0; // Сначала посмотрим пересечение с Before.Grid предыдущей строки if (nCurGridCol <= oPrevBefore.Grid - 1) { var oBorder = this.private_ResolveBordersConflict(oTableBorders.Left, oCellBorders.Top, true, false); var nBorderW = oBorder.GetWidth(); if (nMaxTopBorder < nBorderW) nMaxTopBorder = nBorderW; for (var nCurGrid = 0, nGridCount = Math.min(oPrevBefore.Grid - nCurGridCol, nGridSpan); nCurGrid < nGridCount; ++nCurGrid) arrBorderTopInfo.push(oBorder); } if (-1 !== nPrevPos) { while (nPrevGridCol <= nCurGridCol + nGridSpan - 1 && nPrevPos < nPrevCellsCount) { var oPrevCell = oPrevRow.GetCell(nPrevPos); var nPrevGridSpan = oPrevCell.GetGridSpan(); // Если данная ячейка участвует в вертикальном объединении, тогда нам нужно использовать нижнюю ячейку // в этом объединении, но в не в случае, когда мы расчитываем границу соприкасающуюся с заголовком таблицы // TODO: Надо проверить, зачем вообще это тут добавлено, т.к. ячейка предыдущей строки по логике // не должны быть не последней в своем вертикальном объединении if (oPrevRow === this.GetRow(nCurRow - 1) && vmerge_Continue === oPrevCell.GetVMerge()) { if (this.bPresentation) { oPrevCell = this.Internal_Get_StartMergedCell(nCurRow - 1, nPrevGridCol, nPrevGridSpan); } else { oPrevCell = this.Internal_Get_EndMergedCell(nCurRow - 1, nPrevGridCol, nPrevGridSpan); } } let bCheckPrevTopMergedCell = false; // Надо добавить столько раз, сколько колонок находится в пересечении этих двух ячееки var nGridCount = 0; if (nPrevGridCol >= nCurGridCol) { bCheckPrevTopMergedCell = true; if (nPrevGridCol + nPrevGridSpan - 1 > nCurGridCol + nGridSpan - 1) nGridCount = nCurGridCol + nGridSpan - nPrevGridCol; else nGridCount = nPrevGridSpan; } else if (nPrevGridCol + nPrevGridSpan - 1 > nCurGridCol + nGridSpan - 1) { nGridCount = nGridSpan; } else { nGridCount = nPrevGridCol + nPrevGridSpan - nCurGridCol; } var oPrevBottom = oPrevCell.GetBorders().Bottom; if (this.bPresentation) { for (var nCurGrid = 0; nCurGrid < nGridCount; ++nCurGrid) { var oBorder = this.private_ResolveBordersConflict(oPrevBottom, oCellBorders.Top, false, false, bCheckPrevTopMergedCell ? !nCurGrid : false, !arrBorderTopInfo.length); var nBorderW = oBorder.GetWidth(); if (nMaxTopBorder < nBorderW) nMaxTopBorder = nBorderW; arrBorderTopInfo.push(oBorder); } } else { var oBorder = this.private_ResolveBordersConflict(oPrevBottom, oCellBorders.Top, false, false); var nBorderW = oBorder.GetWidth(); if (nMaxTopBorder < nBorderW) nMaxTopBorder = nBorderW; for (var nCurGrid = 0; nCurGrid < nGridCount; ++nCurGrid) arrBorderTopInfo.push(oBorder); } nPrevPos++; nPrevGridCol += nPrevGridSpan; } } // Посмотрим пересечение с GridAfter предыдущей строки if (oPrevAfter.Grid > 0) { var nStartAfterGrid = oPrevRow.GetCellInfo(nPrevCellsCount - 1).StartGridCol + oPrevRow.GetCell(nPrevCellsCount - 1).GetGridSpan(); if (nCurGridCol + nGridSpan - 1 >= nStartAfterGrid) { var oBorder = this.private_ResolveBordersConflict(oTableBorders.Right, oCellBorders.Top, true, false); var nBorderW = oBorder.GetWidth(); if (nMaxTopBorder < nBorderW) nMaxTopBorder = nBorderW; for (var nCurGrid = 0, nGridCount = Math.min(nCurGridCol + nGridSpan - nStartAfterGrid, nGridSpan); nCurGrid < nGridCount; ++nCurGrid) arrBorderTopInfo.push(oBorder); } } return { Info : arrBorderTopInfo, Max : nMaxTopBorder }; }; CTable.prototype.private_RecalculateHeader = function() { // Если у нас таблица внутри таблицы, тогда в ней заголовочных строк не должно быть, // потому что так делает Word. if (!this.Parent || true === this.Parent.IsTableCellContent()) { 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.IsHeader() ) 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.IsEmptyPage(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); } let maxTopBorder = this.Get_MaxTopBorder(FirstRow); let contentFrame = this.GetPageContentFrame(CurPage); this.Pages.length = CurPage + 1; let yLimit = contentFrame.YLimit; if (0 === CurPage && !this.IsInline() && c_oAscVAnchor.Text === this.PositionV.RelativeFrom && !this.PositionV.Align) { yLimit -= this.PositionV.Value; } this.Pages[CurPage] = new CTablePage(contentFrame.X, contentFrame.Y, contentFrame.XLimit, yLimit, FirstRow, maxTopBorder); }; CTable.prototype.private_RecalculatePositionX = function(CurPage) { var isHdtFtr = this.Parent.IsHdrFtr(); var TablePr = this.Get_CompiledPr(false).TablePr; var PageLimits = this.Parent.Get_PageLimits(this.PageNum); var PageFields = this.Parent.Get_PageFields(this.PageNum, isHdtFtr, this.Get_SectPr()); var LD_PageLimits = this.LogicDocument.Get_PageLimits(this.GetAbsoluteStartPage()); var LD_PageFields = this.LogicDocument.Get_PageFields(this.GetAbsoluteStartPage(), isHdtFtr); let tableInd = TablePr.TableInd; if ( true === this.Is_Inline() ) { var Page = this.Pages[CurPage]; if (0 === CurPage) { this.AnchorPosition.CalcX = this.X_origin + 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.GetTableOffsetCorrection() + tableInd; break; } case AscCommon.align_Right : { var TableWidth = this.TableSumGrid[this.TableSumGrid.length - 1]; if (false === this.Parent.IsTableCellContent()) Page.X = Page.XLimit - TableWidth + this.GetRightTableOffsetCorrection(); 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 = this.GetTableOffsetCorrection(); var OffsetCorrection_Right = this.GetRightTableOffsetCorrection(); this.X = this.X_origin + OffsetCorrection_Left; 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); // Непонятно по какой причине, но Word для плавающих таблиц добаляется значение TableInd this.AnchorPosition.Calculate_X(this.PositionH.RelativeFrom, this.PositionH.Align, this.PositionH.Value); this.AnchorPosition.CalcX += tableInd; this.X = this.AnchorPosition.CalcX; this.X_origin = this.X - OffsetCorrection_Left; 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 - OffsetCorrection_Left; 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.IsTableCellContent(); var oTopDocument = this.Parent.Is_TopDocument(true); var isTopLogicDocument = (oTopDocument instanceof CDocument ? true : false); var oFootnotes = (isTopLogicDocument && !isInnerTable ? oTopDocument.Footnotes : null); var nPageAbs = this.GetAbsolutePage(CurPage); var nColumnAbs = this.GetAbsoluteColumn(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.IsEmptyPage(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 oLogicDocument = this.GetLogicDocument(); if (oLogicDocument && oLogicDocument.IsDocumentEditor() && this.IsInline()) { var nTableX_min = -1; var nTableX_max = -1; for (var nCurRow = 0, nRowsCount = this.GetRowsCount(); nCurRow < nRowsCount; ++nCurRow) { var oRow = this.GetRow(nCurRow); var nCellsCount = oRow.GetCellsCount(); if (!nCellsCount) continue; var nRowX_min = oRow.GetCell(0).Metrics.X_content_start; var nRowX_max = oRow.GetCell(nCellsCount - 1).Metrics.X_content_end; if (-1 === nTableX_min || nRowX_min < nTableX_min) nTableX_min = nRowX_min; if (-1 === nTableX_max || nRowX_max > nTableX_max) nTableX_max = nRowX_max; } nTableX_min += Page.X; nTableX_max += Page.X; var arrRanges = this.Parent.CheckRange(nTableX_min, Page.Y, nTableX_max, Page.Y + 0.001, Page.Y, Page.Y + 0.001, nTableX_min, nTableX_max, this.GetRelativePage(CurPage)); if (arrRanges.length > 0) { for (var nRangeIndex = 0, nRangesCount = arrRanges.length; nRangeIndex < nRangesCount; ++nRangeIndex) { if (Y < arrRanges[nRangeIndex].Y1) { var nShiftY = arrRanges[nRangeIndex].Y1 - Y; Y = arrRanges[nRangeIndex].Y1 + 0.001; Page.Y = Y; Page.Bounds.Top += nShiftY; } } } } var TableBorders = this.Get_Borders(); var nHeaderMaxTopBorder = -1; var X_max = -1; var X_min = -1; if (this.HeaderInfo.Count > 0 && this.HeaderInfo.PageIndex != -1 && CurPage > this.HeaderInfo.PageIndex && this.IsInline()) { this.HeaderInfo.HeaderRecalculate = true; this.HeaderInfo.Pages[CurPage] = {}; this.HeaderInfo.Pages[CurPage].RowsInfo = []; var HeaderPage = this.HeaderInfo.Pages[CurPage]; // Рисуем ли заголовок на данной странице HeaderPage.Draw = true; this.LogicDocument.RecalcTableHeader = true; this.private_RecalculatePrepareHeaderPageRows(HeaderPage); 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.GetVMerge(); var CellMar = Cell.GetMargins(); 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.GetMargins(); Y_content_start = Cell.Temp.Y + CellMar.Top.W; } } if (true === Cell.IsVerticalText()) { 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); Cell.Content.RecalculateEndInfo(); 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 || Asc.linerule_Exact === 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.GetMargins(); 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.GetRelativeStartPage(); 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.GetVMerge(); // Возьмем верхнюю ячейку теккущего объединения 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.GetMargins(); var VAlign = Cell.Get_VAlign(); var CellPageIndex = CurPage - Cell.Content.GetRelativeStartPage(); 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.IsVerticalText()) { 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.ShiftCellContent(CellPageIndex, 0, Dy); } Cell.Temp.Y_VAlign_offset[CellPageIndex] = Dy; } } } nHeaderMaxTopBorder = this.private_GetMaxTopBorderWidth(FirstRow, true); this.LogicDocument.RecalcTableHeader = false; } else { this.HeaderInfo.Pages[CurPage] = {}; this.HeaderInfo.Pages[CurPage].Draw = false; } this.HeaderInfo.HeaderRecalculate = false; var bNextPage = false; // Блок переменных для учета сносок var nFootnotesHeight = 0; var arrSavedY = []; var arrSavedTableHeight = []; var arrFootnotesObject = []; var nResetFootnotesIndex = -1; var nCompatibilityMode = oLogicDocument && oLogicDocument.GetCompatibilityMode ? oLogicDocument.GetCompatibilityMode() : AscCommon.document_compatibility_mode_Current; for (var CurRow = FirstRow; CurRow < this.Content.length; ++CurRow) { if (oFootnotes && (-1 === nResetFootnotesIndex || CurRow > nResetFootnotesIndex)) { nFootnotesHeight = oFootnotes.GetHeight(nPageAbs, nColumnAbs); arrFootnotesObject[CurRow] = oFootnotes.SaveRecalculateObject(nPageAbs, nColumnAbs); arrSavedY[CurRow] = Y; arrSavedTableHeight[CurRow] = TableHeight; this.Pages[CurPage].FootnotesH = nFootnotesHeight; } if ((0 === CurRow && true === this.Check_EmptyPages(CurPage - 1)) || CurRow != FirstRow || (CurRow === FirstRow && true === ResetStartElement)) { this.RowsInfo[CurRow] = new CTableRowsInfo(); this.RowsInfo[CurRow].StartPage = CurPage; this.TableRowsBottom[CurRow] = []; } else { this.RowsInfo[CurRow].Pages = CurPage - this.RowsInfo[CurRow].StartPage + 1; } 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; // Данная ширина используется для проверки влезает ли строка на страницу, т.к. если данная строка последняя // и нужно нарисовать границу у данной строки, то мы можем не убраться именно из-за толщины нижней границы // MaxBotBorder - это расчитанная ширина нижней границы строки, если не учитывать разбивку на страницы let rowMaxBotBorder = 0; var nMaxTopBorder = MaxTopBorder[CurRow]; if (CurRow === FirstRow && nHeaderMaxTopBorder > 0) nMaxTopBorder = nHeaderMaxTopBorder; if (this.private_IsVMergedRow(CurRow) && CurRow < this.Content.length - 1) { this.RowsInfo[CurRow].FirstPage = true; this.RowsInfo[CurRow].Y[CurPage] = Y; this.RowsInfo[CurRow].TopDy[CurPage] = 0; this.RowsInfo[CurRow].X0 = Row.Metrics.X_min; this.RowsInfo[CurRow].X1 = Row.Metrics.X_max; this.RowsInfo[CurRow].MaxTopBorder[CurPage] = 0; this.RowsInfo[CurRow].MaxBotBorder = 0; this.RowsInfo[CurRow].H[CurPage] = 0; this.RowsInfo[CurRow].VMerged = true; for (let iCell = 0, nCells = Row.GetCellsCount(); iCell < nCells; ++iCell) { Row.Update_CellInfo(iCell); let cell = Row.GetCell(iCell); cell.Temp.Y = Y; } continue; } // Добавляем ширину верхней границы у текущей строки if(!this.bPresentation) { Y += nMaxTopBorder; TableHeight += nMaxTopBorder; } // Если таблица с расстоянием между ячейками, тогда добавляем его 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.GetVMerge(); var CellMar = Cell.GetMargins(); if (vmerge_Restart === Vmerge && CellMar.Top.W > MaxTopMargin) MaxTopMargin = CellMar.Top.W; } var RowH = Row.Get_Height(); var RowHValue; if(!this.bPresentation) { // В данном значении не учитываются маргины RowHValue = RowH.Value + this.MaxBotMargin[CurRow] + MaxTopMargin; } else { RowHValue = RowH.Value; } // В таблице с отступами размер отступа входит в значение высоты строки if (null !== CellSpacing) RowHValue -= CellSpacing; // Для строк с точной высотой строк значение высоты считается вместе с шириной верхней границы if (Asc.linerule_Exact === RowH.HRule) RowHValue -= nMaxTopBorder; 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; var bFootnoteBreak = false; let Y_content_end_row = this.Pages[CurPage].YLimit - nFootnotesHeight; if (null != CellSpacing) { if (this.Content.length - 1 === CurRow) Y_content_end_row -= CellSpacing; else Y_content_end_row -= CellSpacing / 2; } for ( var CurCell = 0; CurCell < CellsCount; CurCell++ ) { var Cell = Row.Get_Cell( CurCell ); var GridSpan = Cell.Get_GridSpan(); var Vmerge = Cell.GetVMerge(); var CellMar = Cell.GetMargins(); Row.Update_CellInfo(CurCell); // Обновляем сразу EndInfo, т.к. мы можем начать пересчет следующей ячейки, до окончания полного пересчета // предыдущей ячейки в строке. Кроме того пересчет EndInfo внутри параграфа, в любом случае, выполняется // не более одного раза за текущий Document.RecalcId, поэтому, можем не боятся, что пересчет EndInfo // вызовется несколько раз для параграфа Cell.Content.RecalculateEndInfo(); 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 = Y_content_end_row; // TODO: При расчете YLimit для ячейки сделать учет толщины нижних // границ ячейки и таблицы var VMergeCount = this.Internal_GetVertMergeCount( CurRow, CurGridCol, GridSpan ); var BottomMargin = this.MaxBotMargin[CurRow + VMergeCount - 1]; Y_content_end -= BottomMargin; // Такие ячейки мы обсчитываем, если либо сейчас происходит переход на новую страницу, либо // это последняя ячейка в объединении. // Обсчет такик ячеек произошел ранее let clipTop = undefined; let clipBottom = undefined; if (Asc.linerule_Exact === RowH.HRule) { clipTop = Y; clipBottom = Y + RowHValue; } Cell.Temp.Y = Y_content_start; Cell.Temp.ClipTop = Y; Cell.Temp.ClipBottom = Y + RowHValue; Cell.Temp.ClipPage = CurPage; // Сохраняем ссылку на исходную ячейку var oOriginCell = Cell; 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.GetMargins(); var oTempRow = Cell.GetRow(); var oTempCellMetrics = oTempRow.GetCellInfo(Cell.GetIndex()); X_content_start = Page.X + oTempCellMetrics.X_content_start; X_content_end = Page.X + oTempCellMetrics.X_content_end; Y_content_start = Cell.Temp.Y + CellMar.Top.W; // TODO: Клип пока выставляем для первой страницы ячейки, по логике надо просто выставлять клип на // ячейку на текущей странице if (undefined !== Cell.Temp.ClipTop && CurPage === Cell.Temp.ClipPage) { clipTop = Cell.Temp.ClipTop; Cell.Temp.ClipBottom = clipBottom; } } } Cell.Temp.UseClip = false; if (true === Cell.IsVerticalText()) { VerticallCells.push(Cell); CurGridCol += GridSpan; continue; } // Для случая, когда смерженная вертикально ячейка идет в строке не с точной высотой, а заканчивается // в строке с точной высотой строки if (Asc.linerule_Exact === RowH.HRule) Cell.Temp.UseClip = true; if (null === CellSpacing) { let bottomBorder = this.private_ResolveBordersConflict(Cell.GetBottomBorder(), TableBorders.Bottom, false, true); if (!bottomBorder.IsNone() && rowMaxBotBorder < bottomBorder.GetSize()) rowMaxBotBorder = bottomBorder.GetSize(); } 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 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.GetFootnotesList(null, null, false); var arrEndnotes = Cell.Content.GetFootnotesList(null, null, true); if ((arrFootnotes && arrFootnotes.length > 0) || (arrEndnotes && arrEndnotes.length > 0)) { bCanShift = false; } } } if (Asc.linerule_Exact === RowH.HRule) Y_content_end = Asc.NoYLimit; 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.GetRelativeStartPage(); Cell.Content.Set_ClipInfo(CellPageIndex, Page.X + CellMetrics.X_cell_start, Page.X + CellMetrics.X_cell_end, clipTop, clipBottom); if ( CellPageIndex < Cell.PagesCount ) { if ( true === bCanShift ) { Cell.ShiftCell(0, ShiftDx, ShiftDy); Cell.Content.Set_ClipInfo(CellPageIndex, Page.X + CellMetrics.X_cell_start, Page.X + CellMetrics.X_cell_end, clipTop, clipBottom); 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; } } var nCurFootnotesHeight = oFootnotes ? oFootnotes.GetHeight(nPageAbs, nColumnAbs) : 0; if (oFootnotes && nCurFootnotesHeight > nFootnotesHeight + 0.001) { this.Pages[CurPage].FootnotesH = nCurFootnotesHeight; nFootnotesHeight = nCurFootnotesHeight; nResetFootnotesIndex = CurRow; Y = arrSavedY[oOriginCell.Row.Index]; TableHeight = arrSavedTableHeight[oOriginCell.Row.Index]; oFootnotes.LoadRecalculateObject(nPageAbs, nColumnAbs, arrFootnotesObject[oOriginCell.Row.Index]); CurRow = oOriginCell.Row.Index - 1; bFootnoteBreak = true; break; } CurGridCol += GridSpan; } if (oFootnotes && (Asc.linerule_AtLeast === RowH.HRule || Asc.linerule_Exact == RowH.HRule)) { oFootnotes.PopCellLimit(); } if (bFootnoteBreak) continue; if (undefined === this.TableRowsBottom[CurRow][CurPage]) this.TableRowsBottom[CurRow][CurPage] = Y; // Если в строке все ячейки с вертикальным выравниванием if (bAllCellsVertical && Asc.linerule_Auto === RowH.HRule) this.TableRowsBottom[CurRow][CurPage] = Y + 4.5 + this.MaxBotMargin[CurRow] + MaxTopMargin; if (Asc.linerule_Exact === RowH.HRule) this.TableRowsBottom[CurRow][CurPage] = Y + RowHValue; if ((Asc.linerule_AtLeast === RowH.HRule || Asc.linerule_Exact === RowH.HRule) && AscCommon.MMToTwips(Y + RowHValue + rowMaxBotBorder, 1) >= AscCommon.MMToTwips(Y_content_end_row, -1) && ((0 === CurRow && 0 === CurPage && null !== this.Get_DocumentPrev() && !this.Parent.IsFirstElementOnPage(this.GetRelativePage(CurPage), this.GetIndex())) || CurRow !== FirstRow)) { bNextPage = true; for ( var CurCell = 0; CurCell < CellsCount; CurCell++ ) { var Cell = Row.Get_Cell( CurCell ); var Vmerge = Cell.GetVMerge(); 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 ) { // TODO: Здесь происходит расчет параметра FirstPage для строки, на которой произошел разрыв страницы // Непонятная ситуация, если строка разывается и на следующей странице, то параметр высчитывается // зачем-то заново с учетом текущей, хотя он имеет смысл только для первой страницы. Надо разобраться var bContentOnFirstPage = false; var bNoContentOnFirstPage = false; for ( var CurCell = 0; CurCell < CellsCount; CurCell++ ) { var Cell = Row.Get_Cell( CurCell ); var Vmerge = Cell.GetVMerge(); var VMergeCount = this.Internal_GetVertMergeCount( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() ); // Проверяем только начальные ячейки вертикального объединения.. if ( vmerge_Continue === Vmerge || VMergeCount > 1 ) continue; if (true === Cell.IsVerticalText() || true === Cell.Content_Is_ContentOnFirstPage()) { bContentOnFirstPage = true; } else { bNoContentOnFirstPage = true; } } if (CurRow > 0 && (CurRow > FirstRow || CurPage > 0) && this.RowsInfo[CurRow - 1].VMerged) { bContentOnFirstPage = true; bNoContentOnFirstPage = false; } if ( true === bContentOnFirstPage && true === bNoContentOnFirstPage ) { for ( var CurCell = 0; CurCell < CellsCount; CurCell++ ) { var Cell = Row.Get_Cell( CurCell ); var Vmerge = Cell.GetVMerge(); 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; var bFootnoteBreak = false; 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.IsVerticalText()) { VerticallCells.push(Cell); CurGridCol += GridSpan; continue; } var CellMar = Cell.GetMargins(); 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 - 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; 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.GetRelativeStartPage(); 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 (Asc.linerule_Exact !== RowH.HRule) { if (undefined === this.TableRowsBottom[CurRow][CurPage] || this.TableRowsBottom[CurRow][CurPage] < CellContentBounds_Bottom) this.TableRowsBottom[CurRow][CurPage] = CellContentBounds_Bottom; } } // Проверяем наличие сносок, т.к. они могли появится в смерженных ячейках var nCurFootnotesHeight = oFootnotes ? oFootnotes.GetHeight(nPageAbs, nColumnAbs) : 0; if (oFootnotes && nCurFootnotesHeight > nFootnotesHeight + 0.001 && Cell.Row.Index >= FirstRow) { this.Pages[CurPage].FootnotesH = nCurFootnotesHeight; nFootnotesHeight = nCurFootnotesHeight; nResetFootnotesIndex = CurRow; Y = arrSavedY[Cell.Row.Index]; TableHeight = arrSavedTableHeight[Cell.Row.Index]; oFootnotes.LoadRecalculateObject(nPageAbs, nColumnAbs, arrFootnotesObject[Cell.Row.Index]); CurRow = Cell.Row.Index - 1; bFootnoteBreak = true; break; } CurGridCol += GridSpan; } if (bFootnoteBreak) continue; // Еще раз обновляем параметр, есть ли текст на первой странице bContentOnFirstPage = false; for ( var CurCell = 0; CurCell < CellsCount; CurCell++ ) { var Cell = Row.Get_Cell( CurCell ); var Vmerge = Cell.GetVMerge(); // Проверяем только начальные ячейки вертикального объединения.. if ( vmerge_Continue === Vmerge ) continue; if (true === Cell.IsVerticalText()) continue; if ( true === Cell.Content_Is_ContentOnFirstPage() ) { bContentOnFirstPage = true; break; } } if (CurRow > 0 && (CurRow > FirstRow || CurPage > 0) && this.RowsInfo[CurRow - 1].VMerged) bContentOnFirstPage = true; 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 = nMaxTopBorder; 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 || Asc.linerule_Exact === RowH.HRule) && CellHeight < RowHValue && (nFootnotesHeight < 0.001 || Y + RowHValue + rowMaxBotBorder < 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.GetMargins(); 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.GetRelativeStartPage(); 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; nResetFootnotesIndex = CurRow; Y = arrSavedY[CurRow]; TableHeight = arrSavedTableHeight[CurRow]; oFootnotes.LoadRecalculateObject(nPageAbs, nColumnAbs, arrFootnotesObject[CurRow]); CurRow--; continue; } 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; if ((CurRow < this.HeaderInfo.Count || (CurRow === this.HeaderInfo.Count && !this.RowsInfo[CurRow].FirstPage && nCompatibilityMode >= AscCommon.document_compatibility_mode_Word14)) && (0 === CurPage && null !== this.Get_DocumentPrev() && !this.Parent.IsFirstElementOnPage(this.GetRelativePage(CurPage), this.GetIndex()))) { this.HeaderInfo.PageIndex = -1; LastRow = 0; this.RowsInfo[0].FirstPage = false; this.Pages[CurPage].LastRow = 0; } 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.GetVMerge(); // Возьмем верхнюю ячейку текущего объединения if ( vmerge_Restart != Vmerge ) { Cell = this.Internal_Get_StartMergedCell( CurRow, Cell.Metrics.StartGridCol, Cell.Get_GridSpan() ); } } var CellMar = Cell.GetMargins(); var VAlign = Cell.Get_VAlign(); let cellContent = Cell.GetContent(); var CellPageIndex = CurPage - cellContent.GetRelativeStartPage(); if ( CellPageIndex >= Cell.PagesCount ) continue; // Рассчитаем имеющуюся в распоряжении высоту ячейки var TempCurRow = Cell.Row.Index; // Для прилегания к верху или для второй страницы ничего не делаем (так изначально рассчитывалось) let topAlign = (vertalignjc_Top === VAlign); if (!topAlign) { for (let tmpCellPage = 0; tmpCellPage < CellPageIndex; ++tmpCellPage) { if (!cellContent.IsEmptyPage(tmpCellPage)) { topAlign = true; break; } } } if (topAlign) { Cell.Temp.Y_VAlign_offset[CellPageIndex] = 0; continue; } var TempCellSpacing = this.Content[TempCurRow].Get_CellSpacing(); var Y_0 = this.RowsInfo[TempCurRow].Y[CurPage]; if(!this.bPresentation) { 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.IsVerticalText()) { var CellMetrics = Row.Get_CellInfo(CurCell); CellHeight = CellMetrics.X_cell_end - CellMetrics.X_cell_start - CellMar.Left.W - CellMar.Right.W; } // В версиях совместимости до 14, если текст в ячейке повернут, то выравнивание идет по центру, даже если // содержимое не убирается в пределах ячейки. В более поздних версиях всегда к верху выравнивание, в такой // ситуации if (CellHeight - ContentHeight > 0.001 || (nCompatibilityMode <= AscCommon.document_compatibility_mode_Word14 && Cell.IsVerticalText())) { if (vertalignjc_Bottom === VAlign) Dy = CellHeight - ContentHeight; else if (vertalignjc_Center === VAlign) Dy = (CellHeight - ContentHeight) / 2; Cell.ShiftCellContent(CellPageIndex, 0, Dy, true); } 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.GetVMerge()) Cell = this.Internal_Get_StartMergedCell(CurRow, Row.Get_CellInfo(CurCell).StartGridCol, Cell.Get_GridSpan()); var Border_Info = Cell.GetBorderInfo().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.GetVMerge()) { 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.private_ResolveBordersConflict(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_RecalculatePrepareHeaderPageRows = function(headerPage) { headerPage.Rows = []; let self = this; AscCommon.ExecuteNoHistory(function() { let drawingObjects = []; for (var index = 0; index < self.HeaderInfo.Count; ++index) { headerPage.Rows[index] = self.Content[index].Copy(self); headerPage.Rows[index].SetIndex(index); headerPage.Rows[index].GetAllDrawingObjects(drawingObjects); } for (let index = 0, count = drawingObjects.length; index < count; ++index) { let drawing = drawingObjects[index]; if (!drawing || !drawing.GraphicObj) continue; drawing.GraphicObj.recalculate(); if (drawing.GraphicObj.recalculateText) drawing.GraphicObj.recalculateText(); } }, this.LogicDocument); }; CTable.prototype.private_RecalculatePositionY = function(CurPage) { var isHdrFtr = this.Parent.IsHdrFtr(); var nPageRel = this.GetRelativePage(CurPage); var nPageAbs = this.GetAbsolutePage(CurPage); var PageLimits = this.Parent.Get_PageLimits(nPageRel); var PageFields = this.Parent.Get_PageFields(nPageRel, isHdrFtr); var LD_PageFields = this.LogicDocument.Get_PageFields(nPageAbs, isHdrFtr); var LD_PageLimits = this.LogicDocument.Get_PageLimits(nPageAbs); 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.GetAbsoluteStartPage() ) : []; 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_RecalculatePercentWidth = function() { return this.TableWidthRange - this.GetTableOffsetCorrection() + this.GetRightTableOffsetCorrection(); }; CTable.prototype.private_RecalculateGridCols = function() { for (var nCurRow = 0, nRowsCount = this.Content.length; nCurRow < nRowsCount; ++nCurRow) { var oRow = this.Content[nCurRow]; var oBeforeInfo = oRow.Get_Before(); var nCurGridCol = oBeforeInfo.GridBefore; for (var nCurCell = 0, nCellsCount = oRow.GetCellsCount(); nCurCell < nCellsCount; ++nCurCell) { var oCell = oRow.GetCell(nCurCell); oRow.Set_CellInfo(nCurCell, nCurGridCol, 0, 0, 0, 0, 0, 0); oCell.Set_Metrics(nCurGridCol, 0, 0, 0, 0, 0, 0); nCurGridCol += oCell.Get_GridSpan(); } } }; CTable.prototype.private_GetMaxTopBorderWidth = function(nCurRow, isHeader) { var nMax = 0; var oRow = this.GetRow(nCurRow); for (var nCurCell = 0, nCellsCount = oRow.GetCellsCount(); nCurCell < nCellsCount; ++nCurCell) { var oCell = oRow.GetCell(nCurCell); var arrBorderInfo = isHeader ? oCell.GetBorderInfo().TopHeader : oCell.GetBorderInfo().Top; for (var nInfoIndex = 0, nInfosCount = arrBorderInfo.length; nInfoIndex < nInfosCount; ++nInfoIndex) { var nBorderW = arrBorderInfo[nInfoIndex].GetWidth(); if (nMax < nBorderW) nMax = nBorderW; } } return nMax; }; CTable.prototype.private_IsVMergedRow = function(iRow) { let row = this.GetRow(iRow); let curGridCol = row.GetBefore().Grid; for (let iCell = 0, nCells = row.GetCellsCount(); iCell < nCells; ++iCell) { let cell = row.GetCell(iCell); let gridSpan = cell.GetGridSpan(); if (this.Internal_GetVertMergeCount(iRow, curGridCol, gridSpan) <= 1) return false; curGridCol += gridSpan; } return true; }; //---------------------------------------------------------------------------------------------------------------------- // Класс 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; this.MaxBotBorder = 0; this.BotBorders = []; } CTablePage.prototype.Shift = function(Dx, Dy) { this.X += Dx; this.Y += Dy; this.XLimit += Dx; this.YLimit += Dy; this.Bounds.Shift(Dx, Dy); }; CTablePage.prototype.GetFirstRow = function() { return this.FirstRow; }; CTablePage.prototype.GetLastRow = function() { return this.LastRow; }; //---------------------------------------------------------------------------------------------------------------------- // Класс CTableRecalcInfo //---------------------------------------------------------------------------------------------------------------------- function CTableRecalcInfo() { this.TableGrid = true; this.TableBorders = true; this.CellsToRecalc = {}; this.CellsAll = true; } CTableRecalcInfo.prototype.RecalcBorders = 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(isNeedRecalculate) { this.TableGrid = isNeedRecalculate; this.TableBorders = isNeedRecalculate; this.CellsAll = isNeedRecalculate; 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); } };