282 lines
9.8 KiB
JavaScript
282 lines
9.8 KiB
JavaScript
/*
|
||
* (c) Copyright Ascensio System SIA 2010-2025
|
||
*
|
||
* 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";
|
||
|
||
|
||
(function()
|
||
{
|
||
/**
|
||
* @constructor
|
||
*/
|
||
function DocumentPageSection()
|
||
{
|
||
this.Pos = 0;
|
||
this.EndPos = -1;
|
||
|
||
this.ResetSectionStart = false; // Если секция разбилась внутри блочного контент контрола
|
||
|
||
this.Y = 0;
|
||
this.YLimit = 0;
|
||
|
||
this.YLimit2 = 0;
|
||
|
||
this.Index = -1;
|
||
this.SectPr = null;
|
||
|
||
this.Columns = [];
|
||
this.ColumnsSep = false;
|
||
|
||
this.IterationsCount = 0;
|
||
this.CurrentY = 0;
|
||
this.RecalculateBottomLine = true;
|
||
this.CanDecrease = true;
|
||
this.WasIncrease = false; // Было ли хоть раз увеличение
|
||
this.IterationStep = 10;
|
||
this.IterationDirection = 0;
|
||
}
|
||
/**
|
||
* Инициализируем параметры данной секции
|
||
* @param {number} nPageAbs
|
||
* @param {AscWord.SectPr} oSectPr
|
||
* @param {Number} nSectionIndex
|
||
*/
|
||
DocumentPageSection.prototype.Init = function(nPageAbs, oSectPr, nSectionIndex)
|
||
{
|
||
var oFrame = oSectPr.GetContentFrame(nPageAbs);
|
||
var nX = oFrame.Left;
|
||
var nXLimit = oFrame.Right;
|
||
|
||
for (var nCurColumn = 0, nColumnsCount = oSectPr.GetColumnCount(); nCurColumn < nColumnsCount; ++nCurColumn)
|
||
{
|
||
this.Columns[nCurColumn] = new AscWord.DocumentPageColumn();
|
||
|
||
this.Columns[nCurColumn].X = nX;
|
||
this.Columns[nCurColumn].XLimit = nColumnsCount - 1 === nCurColumn ? nXLimit : nX + oSectPr.GetColumnWidth(nCurColumn);
|
||
|
||
nX += oSectPr.GetColumnWidth(nCurColumn) + oSectPr.GetColumnSpace(nCurColumn);
|
||
}
|
||
this.ColumnsSep = oSectPr.GetColumnSep();
|
||
|
||
this.Y = oFrame.Top;
|
||
this.YLimit = oFrame.Bottom;
|
||
this.YLimit2 = oFrame.Bottom;
|
||
this.Index = nSectionIndex;
|
||
this.SectPr = oSectPr;
|
||
};
|
||
DocumentPageSection.prototype.GetIndex = function()
|
||
{
|
||
return this.Index;
|
||
};
|
||
DocumentPageSection.prototype.GetSectPr = function()
|
||
{
|
||
return this.SectPr;
|
||
};
|
||
DocumentPageSection.prototype.Copy = function()
|
||
{
|
||
var NewSection = new DocumentPageSection();
|
||
|
||
NewSection.Pos = this.Pos;
|
||
NewSection.EndPos = this.EndPos;
|
||
NewSection.Y = this.Y;
|
||
NewSection.YLimit = this.YLimit;
|
||
|
||
for (var ColumnIndex = 0, Count = this.Columns.length; ColumnIndex < Count; ++ColumnIndex)
|
||
{
|
||
NewSection.Columns[ColumnIndex] = this.Columns[ColumnIndex].Copy();
|
||
}
|
||
|
||
return NewSection;
|
||
};
|
||
DocumentPageSection.prototype.Shift = function(Dx, Dy)
|
||
{
|
||
this.Y += Dy;
|
||
this.YLimit += Dy;
|
||
|
||
for (var ColumnIndex = 0, Count = this.Columns.length; ColumnIndex < Count; ++ColumnIndex)
|
||
{
|
||
this.Columns[ColumnIndex].Shift(Dx, Dy);
|
||
}
|
||
};
|
||
DocumentPageSection.prototype.CorrectY = function(y)
|
||
{
|
||
return (y < this.Y ? this.Y : (y > this.YLimit ? this.YLimit : y));
|
||
};
|
||
/**
|
||
* Происходи ли процесс расчета нижней границы разрыва секции на текущей странице
|
||
* @returns {boolean}
|
||
*/
|
||
DocumentPageSection.prototype.IsCalculatingSectionBottomLine = function()
|
||
{
|
||
return (this.IterationsCount > 0 && true === this.RecalculateBottomLine);
|
||
};
|
||
/**
|
||
* Можно ли расчитывать нижнюю границу разрыва секции на текущей странице
|
||
* @returns {boolean}
|
||
*/
|
||
DocumentPageSection.prototype.CanRecalculateBottomLine = function()
|
||
{
|
||
return this.RecalculateBottomLine;
|
||
};
|
||
/**
|
||
* Запрещаем возможность расчета нижней границы разрыва секции на текущей страницы
|
||
*/
|
||
DocumentPageSection.prototype.ForbidRecalculateBottomLine = function()
|
||
{
|
||
this.RecalculateBottomLine = false;
|
||
};
|
||
DocumentPageSection.prototype.GetY = function()
|
||
{
|
||
return this.Y;
|
||
};
|
||
DocumentPageSection.prototype.GetYLimit = function()
|
||
{
|
||
if (0 === this.IterationsCount)
|
||
return this.YLimit;
|
||
else
|
||
return this.CurrentY;
|
||
};
|
||
/**
|
||
* Производим шаг рассчета нижней границы рарзрыва секции
|
||
* @param {boolean} isIncrease
|
||
* @returns {number}
|
||
*/
|
||
DocumentPageSection.prototype.IterateBottomLineCalculation = function(isIncrease)
|
||
{
|
||
// Алгоритм следующий:
|
||
// На первом шаге мы прогнозируем положение границы по уже имеющемуся объему текста и
|
||
// ширине колонок.
|
||
// Далее мы сдвигаем границу на значение IterationStep, вверх или вниз в зависимости
|
||
// от результата расчета страницы. При перемене направления сдвига мы всегда уменьшаем шаг
|
||
// в 2 раза. Также мы можем уменьшать шаг в 2 раза, если сдвигаем границу вверх, и хотябы
|
||
// раз до этого двигали ее вниз. Останавливаем итерацию при попытке подвинуть границу наверх,
|
||
// когда шаг итерации становится менее 2мм.
|
||
|
||
if (0 === this.IterationsCount)
|
||
{
|
||
// Пытаемся заранее спрогнозировать позицию, где должно быть разделение. Учитывая, что колонки могут быть разной
|
||
// ширины, мы расчитываем суммарную занимаемую текстом область. Делим ее по колонкам, с учетом их суммарной
|
||
// ширины.
|
||
|
||
var nSumArea = 0, nSumWidth = 0;
|
||
for (var nColumnIndex = 0, nColumnsCount = this.Columns.length; nColumnIndex < nColumnsCount; ++nColumnIndex)
|
||
{
|
||
var oColumn = this.Columns[nColumnIndex];
|
||
if (true !== oColumn.Empty)
|
||
nSumArea += (oColumn.Bounds.Bottom - this.Y) * (oColumn.XLimit - oColumn.X);
|
||
|
||
nSumWidth += oColumn.XLimit - oColumn.X;
|
||
}
|
||
|
||
if (nSumWidth > 0.001)
|
||
this.CurrentY = this.Y + nSumArea / nSumWidth;
|
||
else
|
||
this.CurrentY = this.Y;
|
||
}
|
||
else
|
||
{
|
||
if (false === isIncrease)
|
||
{
|
||
if (this.IterationDirection > 0 || this.WasIncrease)
|
||
this.IterationStep /= 2;
|
||
|
||
this.CurrentY -= this.IterationStep;
|
||
this.IterationDirection = -1;
|
||
|
||
if (this.CurrentY < this.Y)
|
||
{
|
||
// Такое может быть, когда у нас всего одна строка в начале страницы, которую мы размещаем всегда
|
||
this.CurrentY = this.Y;
|
||
this.CanDecrease = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (this.IterationDirection < 0)
|
||
this.IterationStep /= 2;
|
||
|
||
this.CurrentY += this.IterationStep;
|
||
this.IterationDirection = 1;
|
||
|
||
this.WasIncrease = true;
|
||
}
|
||
}
|
||
|
||
if (this.IterationStep < 2)
|
||
this.CanDecrease = false;
|
||
|
||
this.CurrentY = Math.min(this.CurrentY, this.YLimit2);
|
||
|
||
// // Recalculation LOG
|
||
// console.log(`Calculate continuous section count=${this.IterationsCount} step=${this.IterationStep} Y=${this.CurrentY}`);
|
||
|
||
this.IterationsCount++;
|
||
return this.CurrentY;
|
||
};
|
||
DocumentPageSection.prototype.Reset_Columns = function()
|
||
{
|
||
for (var ColumnIndex = 0, Count = this.Columns.length; ColumnIndex < Count; ++ColumnIndex)
|
||
{
|
||
this.Columns[ColumnIndex].Reset();
|
||
}
|
||
};
|
||
/**
|
||
* Можем ли мы провести еще одну итерацию с уменьшением нижней границы
|
||
* @returns {boolean}
|
||
*/
|
||
DocumentPageSection.prototype.CanDecreaseBottomLine = function()
|
||
{
|
||
return this.CanDecrease;
|
||
};
|
||
DocumentPageSection.prototype.CanIncreaseBottomLine = function()
|
||
{
|
||
// Данная функция не должна возвращать false, если возвращает, значит неправильно работает алгоритм по вычислению
|
||
// нижней границы continuous секции
|
||
// if (!(this.YLimit2 - this.CurrentY > 0.001))
|
||
// console.log("Bad continuous section calculate");
|
||
|
||
return (this.YLimit2 - this.CurrentY > 0.001);
|
||
};
|
||
DocumentPageSection.prototype.GetBottomLimit = function()
|
||
{
|
||
let bottomLimit = this.Y;
|
||
for (let column = 0, columnCount = this.Columns.length; column < columnCount; ++column)
|
||
{
|
||
bottomLimit = Math.max(bottomLimit, this.Columns[column].Bounds.Bottom);
|
||
}
|
||
return bottomLimit;
|
||
};
|
||
//--------------------------------------------------------export----------------------------------------------------
|
||
AscWord.DocumentPageSection = DocumentPageSection;
|
||
|
||
})();
|