4748 lines
177 KiB
JavaScript
4748 lines
177 KiB
JavaScript
/*
|
||
* (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";
|
||
|
||
// Import
|
||
var g_oTextMeasurer = AscCommon.g_oTextMeasurer;
|
||
var c_oAscSectionBreakType = Asc.c_oAscSectionBreakType;
|
||
|
||
// TODO: В колонтитулах быстрые пересчеты отключены. Надо реализовать.
|
||
|
||
/**
|
||
* Здесь мы пытаемся быстро пересчитать текущий параграф. Если быстрый пересчет срабатывает, тогда возвращаются
|
||
* страницы, которые нужно перерисовать, в противном случае возвращается пустой массив.
|
||
* @returns {*}
|
||
*/
|
||
Paragraph.prototype.Recalculate_FastWholeParagraph = function()
|
||
{
|
||
if (this.Pages.length <= 0 || undefined === this.Parent)
|
||
return [];
|
||
|
||
if (true === this.Parent.IsHdrFtr(false))
|
||
return [];
|
||
|
||
//Не запускаемм быстрый пересчет, когда параграф находится в автофигуре с выставленным флагом подбора размера по размеру контента,
|
||
// т. к. при расчете контента потребуется пересчет в новых размерах.
|
||
if(true === this.Parent.Check_AutoFit())
|
||
{
|
||
return [];
|
||
}
|
||
|
||
// TODO: Отключаем это ускорение в таблицах, т.к. в таблицах и так есть свое ускорение. Но можно и это ускорение
|
||
// подключить, для этого надо проверять изменились ли MinMax ширины и набираем ли мы в строке заголовков.
|
||
var oCell = this.IsTableCellContent(true);
|
||
if (oCell && oCell.GetTable())
|
||
{
|
||
if (tbllayout_AutoFit === oCell.GetTable().Get_CompiledPr(false).TablePr.TableLayout || oCell.IsInHeader(true))
|
||
return [];
|
||
}
|
||
|
||
// Если изменения происходят в специальном пустом параграфе-конце секции, тогда запускаем обычный пересчет
|
||
if (this.bFromDocument && this.LogicDocument && (!this.LogicDocument.Pages[this.GetAbsoluteStartPage()] || true === this.LogicDocument.Pages[this.GetAbsoluteStartPage()].Check_EndSectionPara(this)))
|
||
return [];
|
||
|
||
// Если параграф - рамка с автошириной, надо пересчитывать по обычному
|
||
if (1 === this.Lines.length && true !== this.Is_Inline())
|
||
return [];
|
||
|
||
this.SetIsRecalculated(true);
|
||
|
||
// Здесь мы отдельно обрабатываем случаи быстрого пересчета параграфов, которые были разбиты на 1-2
|
||
// страницы. Если параграф был разбит более чем на 2 страницы, то такое ускорение уже не имеет смысла.
|
||
// В обеих ситуациях нужно проверить, что EndInfo остался прежним, иначе требуется пересчет
|
||
if (1 === this.Pages.length)
|
||
{
|
||
// Если параграф был разбит на 1 страницу изначально, тогда мы проверяем, чтобы он после пересчета
|
||
// был также разбит на 1 страницу, кроме этого проверяем изменились ли границы параграфа, и проверяем
|
||
// последнюю строку на pageBreak/columnBreak, а во время пересчета смотрим изменяeтся ли положение
|
||
// flow-объектов, привязанных к данному параграфу, кроме того, если по какой-то причине пересчет возвращает
|
||
// не recalcresult_NextElement, тогда тоже отменяем быстрый пересчет
|
||
|
||
|
||
|
||
var oEndInfo = this.GetEndInfo().Copy();
|
||
var OldBounds = this.Pages[0].Bounds;
|
||
var isPageBreakLastLine1 = this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakPage;
|
||
var isPageBreakLastLine2 = this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakRealPage;
|
||
var FastRecalcResult = this.Recalculate_Page(0, true, true);
|
||
|
||
if (!this.GetEndInfo().IsEqual(oEndInfo))
|
||
return [];
|
||
|
||
if (FastRecalcResult & recalcresult_NextElement
|
||
&& 1 === this.Pages.length
|
||
&& true === this.Pages[0].Bounds.Compare(OldBounds)
|
||
&& this.Lines.length > 0
|
||
&& isPageBreakLastLine1 === (this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakPage)
|
||
&& isPageBreakLastLine2 === (this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakRealPage))
|
||
{
|
||
//console.log("Recalc Fast WholeParagraph 1 page");
|
||
return [this.GetAbsolutePage(0)];
|
||
}
|
||
}
|
||
else if (2 === this.Pages.length)
|
||
{
|
||
var oEndInfo = this.GetEndInfo().Copy();
|
||
|
||
// Если параграф был разбит на 2 страницы изначально, тогда мы проверяем, чтобы он после пересчета
|
||
// был также разбит на 2 страницы, кроме этого проверяем изменились ли границы параграфа на каждой странице,
|
||
// а во время пересчета смотрим изменяeтся ли положение flow-объектов, привязанных к данному параграфу.
|
||
// Кроме того, если по какой-то причине пересчет возвращает не recalcresult_NextPage на первой странице, или не
|
||
// recalcresult_NextElement, тогда тоже отменяем быстрый пересчет.
|
||
var OldBounds_0 = this.Pages[0].Bounds;
|
||
var OldBounds_1 = this.Pages[1].Bounds;
|
||
|
||
var isPageBreakLastLine1 = this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakPage;
|
||
var isPageBreakLastLine2 = this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakRealPage;
|
||
|
||
// Чтобы защититься от неправильной работы, связанной с переносом параграфа на новую страницу,
|
||
// будем следить за тем, начинался ли изначально параграф с новой страницы, и начинается ли он с
|
||
// новой страницы сейчас.
|
||
var OldStartFromNewPage = this.Pages[0].StartLine < 0 ? true : false;
|
||
|
||
// Чтобы защититься от неправильной работой с висячими строками, будем следить за количеством строк
|
||
// если оно меньше либо равно 2 на какой-либо странице до/после пересчета.
|
||
var OldLinesCount_0 = this.Pages[0].EndLine - this.Pages[0].StartLine + 1;
|
||
var OldLinesCount_1 = this.Pages[1].EndLine - this.Pages[1].StartLine + 1;
|
||
|
||
var FastRecalcResult = this.Recalculate_Page(0, true, true);
|
||
|
||
if (!(FastRecalcResult & recalcresult_NextPage))
|
||
return [];
|
||
|
||
FastRecalcResult = this.Recalculate_Page(1, true, true);
|
||
if (!(FastRecalcResult & recalcresult_NextElement))
|
||
return [];
|
||
|
||
// Сравниваем количество страниц (хотя оно должно быть 2 к данному моменту) и границы каждой страницы
|
||
if (2 !== this.Pages.length || true !== this.Pages[0].Bounds.Compare(OldBounds_0) || true !== this.Pages[1].Bounds.Compare(OldBounds_1))
|
||
return [];
|
||
|
||
// Сравниваем наличие pageBreak/columnBreak в последней строке
|
||
if (this.Lines.length <= 0
|
||
|| isPageBreakLastLine1 !== (this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakPage)
|
||
|| isPageBreakLastLine2 !== (this.Lines[this.Lines.length - 1].Info & paralineinfo_BreakRealPage))
|
||
return [];
|
||
|
||
// Проверяем пустую первую страницу
|
||
var StartFromNewPage = this.Pages[0].StartLine < 0 ? true : false;
|
||
if (StartFromNewPage !== OldStartFromNewPage)
|
||
return [];
|
||
|
||
// Если параграф начался с новой страницы, тогда у него не надо проверять висячие строки
|
||
if (true !== StartFromNewPage)
|
||
{
|
||
var LinesCount_0 = this.Pages[0].EndLine - this.Pages[0].StartLine + 1;
|
||
var LinesCount_1 = this.Pages[1].EndLine - this.Pages[1].StartLine + 1;
|
||
|
||
if ((OldLinesCount_0 <= 2 || LinesCount_0 <= 2) && OldLinesCount_0 !== LinesCount_0)
|
||
return [];
|
||
|
||
if ((OldLinesCount_1 <= 2 || LinesCount_1 <= 2) && OldLinesCount_1 !== LinesCount_1)
|
||
return [];
|
||
}
|
||
|
||
if (!this.GetEndInfo().IsEqual(oEndInfo))
|
||
return [];
|
||
|
||
//console.log("Recalc Fast WholeParagraph 2 pages");
|
||
|
||
// Если параграф начинается с новой страницы, тогда не надо перерисовывать первую страницу, т.к. она
|
||
// изначально была пустая, и сейчас пустая.
|
||
|
||
if (true === StartFromNewPage)
|
||
{
|
||
return [this.GetAbsolutePage(1)];
|
||
}
|
||
else
|
||
{
|
||
var PageAbs0 = this.GetAbsolutePage(0);
|
||
var PageAbs1 = this.GetAbsolutePage(1);
|
||
if (PageAbs0 !== PageAbs1)
|
||
return [PageAbs0, PageAbs1];
|
||
else
|
||
return [PageAbs0];
|
||
}
|
||
}
|
||
|
||
return [];
|
||
};
|
||
/**
|
||
* Ивент, если удалось быстро пересчитать параграф
|
||
*/
|
||
Paragraph.prototype.OnFastRecalculate = function()
|
||
{
|
||
let topDocument = this.GetTopDocumentContent();
|
||
if (topDocument && (topDocument instanceof AscWord.FootEndnote))
|
||
topDocument.OnFastRecalculate();
|
||
};
|
||
/**
|
||
* Пытаемся быстро рассчитать отрезок, в котором произошли изменения, и если ничего не съехало, тогда
|
||
* перерисовываем страницу, в противном случаем запускаем обычный пересчет.
|
||
* @param {CParaPos} oParaPos
|
||
* @returns {*} -1 если быстрый пересчет не получился, либо номер страницы, которую нужно перерисовать
|
||
*/
|
||
Paragraph.prototype.RecalculateFastRunRange = function(oParaPos)
|
||
{
|
||
if (this.Pages.length <= 0)
|
||
return -1;
|
||
|
||
if (true === this.Parent.IsHdrFtr(false))
|
||
return -1;
|
||
|
||
if (!oParaPos)
|
||
return -1;
|
||
|
||
var Line = oParaPos.Line;
|
||
var Range = oParaPos.Range;
|
||
|
||
// Такое возможно, если у нас шел долгий пересчет (например, из-за изменений второго пользователя) и в это же время
|
||
// запустился быстрый (ввод символа). Долгий пересчет успел сбросить рассчет данного параграфа, но не пересчитал параграф
|
||
// до конца, а в это время у данного параграфа запросился быстрый пересчет.
|
||
if (this.Lines.length <= oParaPos.Line)
|
||
return -1;
|
||
|
||
// TODO: Отключаем это ускорение в таблицах, т.к. в таблицах и так есть свое ускорение. Но можно и это ускорение
|
||
// подключить, для этого надо проверять изменились ли MinMax ширины и набираем ли мы в строке заголовков.
|
||
if ( undefined === this.Parent || true === this.IsTableCellContent() )
|
||
return -1;
|
||
|
||
//Не запускаемм быстрый пересчет, когда параграф находится в автофигуре с выставленным флагом подбора размера по размеру контента,
|
||
// т. к. при расчете контента потребуется пересчет в новых размерах.
|
||
if(true === this.Parent.Check_AutoFit())
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// Если мы находимся в строке, которая была полностью перенесена из-за обтекания, и мы добавляем пробел, или
|
||
// удаляем символ, тогда нам запускать обычный пересчет, т.к. первое слово может начать убираться в промежутках
|
||
// обтекания, которых у нас нет в отрезках строки
|
||
if ( true === this.Lines[Line].RangeY )
|
||
{
|
||
// TODO: Сделать проверку на добавление пробела и удаление
|
||
return -1;
|
||
}
|
||
|
||
// Если у нас есть PageBreak в строке, запускаем обычный пересчет, либо если это пустой параграф.
|
||
if (this.Lines[Line].Info & paralineinfo_BreakPage || (this.Lines[Line].Info & paralineinfo_Empty && this.Lines[Line].Info & paralineinfo_End))
|
||
return -1;
|
||
|
||
if ( 0 === Line && 0 === Range && undefined !== this.Get_SectionPr() )
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// Если наш параграф является рамкой с авто шириной, тогда пересчитываем по обычному
|
||
// TODO: Улучишить данную проверку
|
||
if ( 1 === this.Lines.length && true !== this.Is_Inline() )
|
||
return -1;
|
||
|
||
|
||
// Мы должны пересчитать как минимум 3 отрезка: текущий, предыдущий и следующий, потому что при удалении элемента
|
||
// или добавлении пробела первое слово в данном отрезке может убраться в предыдущем отрезке, и кроме того при
|
||
// удалении возможен вариант, когда мы неправильно определили отрезок (т.е. более ранний взяли). Но возможен
|
||
// вариант, при котором предыдущий или/и следующий отрезки - пустые, т.е. там нет ни одного текстового элемента
|
||
// тогда мы начинаем проверять с отрезка, в котором есть хоть что-то.
|
||
|
||
var PrevLine = Line;
|
||
var PrevRange = Range;
|
||
|
||
while (PrevLine >= 0)
|
||
{
|
||
PrevRange--;
|
||
|
||
if (PrevRange < 0)
|
||
{
|
||
PrevLine--;
|
||
|
||
if (PrevLine < 0)
|
||
break;
|
||
|
||
PrevRange = this.Lines[PrevLine].Ranges.length - 1;
|
||
}
|
||
|
||
if (!this.IsEmptyRange(PrevLine, PrevRange))
|
||
break;
|
||
}
|
||
|
||
if (PrevLine < 0)
|
||
{
|
||
PrevLine = Line;
|
||
PrevRange = Range;
|
||
}
|
||
|
||
var NextLine = Line;
|
||
var NextRange = Range;
|
||
|
||
var LinesCount = this.Lines.length;
|
||
|
||
while (NextLine <= LinesCount - 1)
|
||
{
|
||
NextRange++;
|
||
|
||
if (NextRange > this.Lines[NextLine].Ranges.length - 1)
|
||
{
|
||
NextLine++;
|
||
|
||
if (NextLine > LinesCount - 1)
|
||
break;
|
||
|
||
NextRange = 0;
|
||
}
|
||
|
||
if (!this.IsEmptyRange(NextLine, NextRange))
|
||
break;
|
||
}
|
||
|
||
if (NextLine > LinesCount - 1)
|
||
{
|
||
NextLine = Line;
|
||
NextRange = Range;
|
||
}
|
||
|
||
// Важно, что здесь мы формируем текст без учета переносов (т.е. не Temporary), но при этом сам флаг
|
||
// Temporary c элементов текста не снимаем, т.к. он рассчитывается уже в процессе Recalculate_Range,
|
||
// а для всех строк, не учавствующих в быстром пересчете, мы должны все сохранить как есть
|
||
this.ShapeText();
|
||
this.ShapeTextInRange(this.Get_StartRangePos2(Line, Range), this.Get_EndRangePos2(Line, Range));
|
||
|
||
this.HyphenateText();
|
||
|
||
// Если у нас отрезок, в котором произошли изменения является отрезком с нумерацией, тогда надо запустить
|
||
// обычный пересчет.
|
||
if (null !== this.Numbering.Item && (PrevLine < this.Numbering.Line || (PrevLine === this.Numbering.Line && PrevRange <= this.Numbering.Range)))
|
||
{
|
||
// TODO: Сделать проверку на само изменение, переместилась ли нумерация
|
||
var CompiledParaPr = this.Get_CompiledPr2(false).ParaPr;
|
||
if (this.Numbering.Type === para_Numbering)
|
||
{
|
||
var NumPr = CompiledParaPr.NumPr;
|
||
if (undefined !== NumPr && undefined !== NumPr.NumId && 0 !== NumPr.NumId && "0" !== NumPr.NumId)
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var Bullet = this.Numbering.Bullet;
|
||
if (Bullet && null !== Bullet.m_oTextPr && null !== Bullet.m_nNum && null != Bullet.m_sString && Bullet.m_sString.length !== 0)
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Если мы дошли до данного места, значит быстрый пересчет отрезка разрешен
|
||
var CurLine = PrevLine;
|
||
var CurRange = PrevRange;
|
||
|
||
// TODO: Для включения данной проверки нужно пробегаться по строке и пересчитывать Line.Info
|
||
// var arrLinesMetrics = [];
|
||
// for (var nLineIndex = 0; nLineIndex <= NextLine - CurLine; ++nLineIndex)
|
||
// {
|
||
// arrLinesMetrics.push(this.Lines[CurLine + nLineIndex].Metrics.Copy());
|
||
// }
|
||
|
||
var Result;
|
||
while ( ( CurLine < NextLine ) || ( CurLine === NextLine && CurRange <= NextRange ) )
|
||
{
|
||
var TempResult = this.recalculateRangeFast(CurRange, CurLine);
|
||
if ( -1 === TempResult )
|
||
return -1;
|
||
|
||
if ( CurLine === Line && CurRange === Range )
|
||
Result = TempResult;
|
||
|
||
CurRange++;
|
||
|
||
if ( CurRange > this.Lines[CurLine].Ranges.length - 1 )
|
||
{
|
||
CurLine++;
|
||
CurRange = 0;
|
||
}
|
||
}
|
||
|
||
// var oParaPr = this.Get_CompiledPr2(false).ParaPr;
|
||
// var oPRS = this.m_oPRSW;
|
||
// for (var nLineIndex = PrevLine; nLineIndex <= NextLine; ++nLineIndex)
|
||
// {
|
||
// oPRS.Reset_Line();
|
||
// this.Lines[nLineIndex].Metrics.Reset();
|
||
// this.private_RecalculateLineMetrics(nLineIndex, oParaPos.Page, oPRS, oParaPr);
|
||
//
|
||
// if (!this.Lines[nLineIndex].Metrics.IsEqual(arrLinesMetrics[nLineIndex - PrevLine]))
|
||
// return null;
|
||
// }
|
||
|
||
// Во время пересчета сбрасываем привязку курсора к строке.
|
||
this.CurPos.Line = -1;
|
||
this.CurPos.Range = -1;
|
||
|
||
this.RequestSpellCheck();
|
||
this.SetIsRecalculated(true);
|
||
|
||
//console.log("Recalc Fast Range");
|
||
|
||
return this.GetAbsolutePage(Result);
|
||
};
|
||
|
||
/**
|
||
* Функция для пересчета страницы параграфа.
|
||
* @param {number} CurPage - Номер страницы, которую нужно пересчитать (относительный номер страницы для параграфа).
|
||
* Предыдущая страница ДОЛЖНА быть пересчитана, если задано не нулевое значение.
|
||
* @param {boolean} isStart - Устаревший параметр, добавлен для совместимости
|
||
* @param {boolean} isFast - быстрый ли пересчет
|
||
* @returns {*} Возвращается результат пересчета
|
||
*/
|
||
Paragraph.prototype.Recalculate_Page = function(CurPage, isStart, isFast)
|
||
{
|
||
if (0 === CurPage)
|
||
{
|
||
this.CalculatedFrame = null;
|
||
this.ShapeText();
|
||
this.HyphenateText();
|
||
}
|
||
|
||
this.Clear_NearestPosArray();
|
||
|
||
// Во время пересчета сбрасываем привязку курсора к строке.
|
||
this.CurPos.Line = -1;
|
||
this.CurPos.Range = -1;
|
||
this.SetIsRecalculated(true);
|
||
|
||
this.FontMap.NeedRecalc = true;
|
||
|
||
this.RequestSpellCheck();
|
||
this.RecalculateEndInfo(isFast, true);
|
||
|
||
var RecalcResult = this.private_RecalculatePage( CurPage, isFast );
|
||
|
||
this.private_CheckColumnBreak(CurPage);
|
||
|
||
this.Parent.RecalcInfo.Reset_WidowControl();
|
||
|
||
if (RecalcResult & recalcresult_NextElement)
|
||
{
|
||
if (window['AscCommon'].g_specialPasteHelper && window['AscCommon'].g_specialPasteHelper.showButtonIdParagraph === this.GetId())
|
||
window['AscCommon'].g_specialPasteHelper.SpecialPasteButtonById_Show();
|
||
|
||
this.UpdateLineNumbersInfo();
|
||
|
||
if (this.Get_SectionPr())
|
||
RecalcResult = recalcresult_NextSection;
|
||
}
|
||
|
||
this.Sections[this.Sections.length - 1].endPage = CurPage;
|
||
|
||
return RecalcResult;
|
||
};
|
||
|
||
/**
|
||
* Функция для пересчета страницы параграфа, так чтобы на данной странице ничего не было. Применяется, когда из-за
|
||
* пересчета плавающей автофигуры нужно сразу перейти на следующую страницу, пропустив несколько колонок.
|
||
* @param {number} PageIndex - Номер страницы, пересчет которой мы пропускаем. (предыдущая страница ДОЛЖНА быть
|
||
* пересчитана, если это не нулевое значение)
|
||
*/
|
||
Paragraph.prototype.Recalculate_SkipPage = function(PageIndex)
|
||
{
|
||
if (0 === PageIndex)
|
||
{
|
||
this.StartFromNewPage();
|
||
}
|
||
else
|
||
{
|
||
var PrevPage = this.Pages[PageIndex - 1];
|
||
|
||
var EndLine = Math.max(PrevPage.StartLine, PrevPage.EndLine); // На случай, если предыдущая страница тоже пустая
|
||
var NewPage = new CParaPage(PrevPage.X, PrevPage.Y, PrevPage.XLimit, PrevPage.YLimit, EndLine);
|
||
NewPage.StartLine = EndLine;
|
||
NewPage.EndLine = EndLine - 1;
|
||
NewPage.TextPr = PrevPage.TextPr;
|
||
|
||
this.Pages[PageIndex] = NewPage;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Функция для сохранения объекта пересчета.
|
||
* @returns {*} Возвращается объект (CParagraphRecalculateObject) с информацией о текущем пересчете параграфа
|
||
*/
|
||
Paragraph.prototype.SaveRecalculateObject = function()
|
||
{
|
||
var RecalcObj = new CParagraphRecalculateObject();
|
||
RecalcObj.Save(this);
|
||
return RecalcObj;
|
||
};
|
||
|
||
/**
|
||
* Загрузка сохраненного раннее пересчета.
|
||
* @param RecalcObj (CParagraphRecalculateObject)
|
||
*/
|
||
Paragraph.prototype.LoadRecalculateObject = function(RecalcObj)
|
||
{
|
||
RecalcObj.Load(this);
|
||
};
|
||
|
||
/**
|
||
* Очистка рассчетных классов параграфа.
|
||
*/
|
||
Paragraph.prototype.PrepareRecalculateObject = function()
|
||
{
|
||
this.Pages = [];
|
||
this.Lines = [];
|
||
|
||
var Count = this.Content.length;
|
||
for (var Index = 0; Index < Count; Index++)
|
||
{
|
||
this.Content[Index].PrepareRecalculateObject();
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Пересчитываем первую страницу параграфа так, чтобы он начинался с новой страницы.
|
||
*/
|
||
Paragraph.prototype.StartFromNewPage = function()
|
||
{
|
||
this.Pages.length = 1;
|
||
this.Pages[0] = new CParaPage(this.X, this.Y, this.XLimit, this.YLimit, 0);
|
||
|
||
// Добавляем разрыв страницы
|
||
this.Pages[0].Set_EndLine(-1);
|
||
this.Lines[-1] = new CParaLine();
|
||
};
|
||
/**
|
||
* Быстрый пересчет заданного отрезка
|
||
* @param {number} iRange - номер отрезка
|
||
* @param {number} iLine - номер строки
|
||
* @returns {number} -1 - если требуется полный пересчет, либо номер текущей страницы
|
||
*/
|
||
Paragraph.prototype.recalculateRangeFast = function(iRange, iLine)
|
||
{
|
||
let wrapState = AscWord.ParagraphStatePool.getWrapState();
|
||
wrapState.SetFast(true);
|
||
let result = this.private_RecalculateFastRange(wrapState, iRange, iLine);
|
||
AscWord.ParagraphStatePool.release(wrapState);
|
||
return result;
|
||
};
|
||
Paragraph.prototype.private_RecalculateFastRange = function(PRS, CurRange, CurLine)
|
||
{
|
||
// Определим номер страницы
|
||
var CurPage = 0;
|
||
var PagesLen = this.Pages.length;
|
||
for ( var TempPage = 0; TempPage < PagesLen; TempPage++ )
|
||
{
|
||
var __Page = this.Pages[TempPage];
|
||
if ( CurLine <= __Page.EndLine && CurLine >= __Page.FirstLine )
|
||
{
|
||
CurPage = TempPage;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( -1 === CurPage )
|
||
return -1;
|
||
|
||
var ParaPr = this.Get_CompiledPr2(false).ParaPr;
|
||
|
||
let contentFrame = this.GetPageContentFrame(CurPage);
|
||
|
||
let XStart = contentFrame.X;
|
||
let YStart = contentFrame.Y;
|
||
let XLimit = contentFrame.XLimit;
|
||
let YLimit = contentFrame.YLimit;
|
||
|
||
PRS.XStart = XStart;
|
||
PRS.YStart = YStart;
|
||
PRS.XLimit = XLimit;// - ParaPr.Ind.Right;
|
||
PRS.YLimit = YLimit;
|
||
|
||
// Обнуляем параметры PRS для строки и отрезка
|
||
PRS.Reset_Line();
|
||
|
||
PRS.Page = CurPage;
|
||
PRS.Line = CurLine;
|
||
PRS.Range = CurRange;
|
||
|
||
PRS.Ranges = this.Lines[CurLine].Ranges;
|
||
PRS.RangesCount = this.Lines[CurLine].Ranges.length - 1;
|
||
|
||
PRS.Paragraph = this;
|
||
|
||
var Line = this.Lines[CurLine];
|
||
var Range = Line.Ranges[CurRange];
|
||
|
||
let nStartPos = Range.StartPos;
|
||
let nEndPos = Range.EndPos;
|
||
|
||
// Обновляем состояние пересчета
|
||
PRS.resetRange(Range);
|
||
|
||
let arrSavedLines = [];
|
||
for (let nPos = nStartPos; nPos <= nEndPos; ++nPos)
|
||
{
|
||
arrSavedLines.push(this.Content[nPos].SaveRecalculateObject(true));
|
||
}
|
||
|
||
for (let nPos = nStartPos; nPos <= nEndPos; ++nPos)
|
||
{
|
||
let oItem = this.Content[nPos];
|
||
if (para_Math === oItem.Type)
|
||
{
|
||
// TODO: Надо бы перенести эту проверку на изменение контента параграфа
|
||
oItem.Set_Inline(true !== this.CheckMathPara(nPos));
|
||
PRS.bFastRecalculate = true; // чтобы не обновить случайно StartLine (Recalculate_Reset)
|
||
}
|
||
|
||
PRS.Update_CurPos(nPos, 0);
|
||
|
||
oItem.Recalculate_Range(PRS, ParaPr, 1);
|
||
|
||
if (PRS.NewRange && PRS.MoveToLBP && PRS.LongWord)
|
||
{
|
||
if (PRS.LineBreakPos.Get(0) !== nEndPos)
|
||
return -1;
|
||
|
||
break;
|
||
}
|
||
else if ((true === PRS.NewRange && nPos !== nEndPos) || (nPos === nEndPos && true !== PRS.NewRange))
|
||
{
|
||
return -1;
|
||
}
|
||
else if (nPos === nEndPos && true === PRS.NewRange && true === PRS.MoveToLBP)
|
||
{
|
||
var BreakPos = PRS.LineBreakPos.Get(0);
|
||
if (BreakPos !== nPos)
|
||
return -1;
|
||
else
|
||
oItem.Recalculate_Set_RangeEndPos(PRS, PRS.LineBreakPos, 1);
|
||
}
|
||
}
|
||
|
||
for (let nPos = nStartPos; nPos <= nEndPos; ++nPos)
|
||
{
|
||
let oLines = arrSavedLines[nPos - nStartPos];
|
||
let oItem = this.Content[nPos];
|
||
|
||
if (!oLines.Compare(CurLine, CurRange, oItem))
|
||
return -1;
|
||
|
||
oItem.LoadRecalculateObject(arrSavedLines[nPos - nStartPos], this);
|
||
}
|
||
|
||
// TODO: Здесь пересчеты идут целиком для строки, а не для конкретного отрезка.
|
||
if (!(this.private_RecalculateLineAlign(CurLine, CurPage, PRS, ParaPr, true) & recalcresult_NextElement))
|
||
return -1;
|
||
|
||
return CurPage;
|
||
};
|
||
Paragraph.prototype.private_RecalculatePage = function(CurPage, isFast)
|
||
{
|
||
let wrapState = AscWord.ParagraphStatePool.getWrapState();
|
||
wrapState.SetFast(isFast);
|
||
let result = this.private_RecalculatePageInternal(wrapState, CurPage, true);
|
||
AscWord.ParagraphStatePool.release(wrapState);
|
||
return result;
|
||
};
|
||
Paragraph.prototype.private_RecalculatePageInternal = function(PRS, CurPage, bFirstRecalculate)
|
||
{
|
||
PRS.Reset_Page(this, CurPage);
|
||
|
||
var Pr = this.Get_CompiledPr();
|
||
var ParaPr = Pr.ParaPr;
|
||
|
||
var CurLine = (CurPage > 0 ? this.Pages[CurPage - 1].EndLine + 1 : 0);
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// Обрабатываем настройку "не отрывать от следующего"
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculatePageKeepNext(CurPage, PRS, ParaPr))
|
||
return PRS.RecalcResult;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// Получаем начальные координаты параграфа
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculatePageXY(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// Делаем проверки, не нужно ли сразу перенести параграф на новую страницу
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculatePageBreak(CurLine, CurPage, PRS,ParaPr))
|
||
{
|
||
this.Recalculate_PageEndInfo(null, CurPage);
|
||
return PRS.RecalcResult;
|
||
}
|
||
|
||
// Изначально обнуляем промежутки обтекания и наличие переноса строки
|
||
PRS.Reset_Ranges();
|
||
|
||
if (false !== bFirstRecalculate)
|
||
{
|
||
PRS.ResetMathRecalcInfo();
|
||
PRS.Reset_MathRecalcInfo();
|
||
PRS.SaveFootnotesInfo();
|
||
}
|
||
else
|
||
{
|
||
PRS.LoadFootnotesInfo();
|
||
}
|
||
|
||
var RecalcResult;
|
||
while (true)
|
||
{
|
||
PRS.Line = CurLine;
|
||
PRS.RecalcResult = recalcresult_NextLine;
|
||
let complexFieldState = PRS.ComplexFields.getState();
|
||
|
||
this.private_RecalculateLine(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
RecalcResult = PRS.RecalcResult;
|
||
|
||
if (RecalcResult & recalcresult_NextLine)
|
||
{
|
||
// В эту ветку мы попадаем, если строка пересчиталась в нормальном режиме и можно переходить к следующей.
|
||
CurLine++;
|
||
|
||
PRS.Reset_Ranges();
|
||
PRS.Reset_RunRecalcInfo();
|
||
PRS.Reset_MathRecalcInfo();
|
||
}
|
||
else if (RecalcResult & recalcresult_ParaMath)
|
||
{
|
||
// В эту ветку попадаем, если нужно заново пересчитать неинлайновую формулу с начала
|
||
CurLine = PRS.resetToMathFirstLine();
|
||
PRS.Reset_RunRecalcInfo();
|
||
}
|
||
else if (RecalcResult & recalcresult_CurLine)
|
||
{
|
||
// В эту ветку мы попадаем, если нам необходимо заново пересчитать данную строку. Такое случается
|
||
// когда у нас появляются плавающие объекты, относительно которых необходимо произвести обтекание.
|
||
// В данном случае мы ничего не делаем, т.к. номер строки не меняется, а новые отрезки обтекания
|
||
// были заполнены при последнем неудачном рассчете.
|
||
|
||
PRS.Restore_RunRecalcInfo();
|
||
PRS.ComplexFields.setState(complexFieldState);
|
||
}
|
||
else if (RecalcResult & recalcresult_NextElement || RecalcResult & recalcresult_NextPage)
|
||
{
|
||
// В эту ветку мы попадаем, если мы достигли конца страницы или конца параграфа. Просто выходим
|
||
// из цикла.
|
||
break;
|
||
}
|
||
else if (RecalcResult & recalcresult_CurPagePara)
|
||
{
|
||
// В эту ветку мы попадаем, если в параграфе встретилась картинка, которая находится ниже данного
|
||
// параграфа, и можно пересчитать заново данный параграф.
|
||
RecalcResult = this.private_RecalculatePageInternal(PRS, CurPage, false);
|
||
break;
|
||
}
|
||
else //if (RecalcResult & recalcresult_CurPage || RecalcResult & recalcresult_PrevPage)
|
||
{
|
||
// В эту ветку мы попадаем, если в нашем параграфе встретилось, что-то из-за чего надо пересчитывать
|
||
// эту страницу или предыдущую страницу. Поэтому далее можно ничего не делать, а сообщать верхнему
|
||
// классу об этом.
|
||
return RecalcResult;
|
||
}
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// Получаем некоторую информацию для следующей страницы (например незакрытые комментарии)
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.Recalculate_PageEndInfo(PRS, CurPage);
|
||
|
||
return RecalcResult;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculatePageKeepNext = function(CurPage, PRS, paraPr)
|
||
{
|
||
if (paraPr.PageBreakBefore)
|
||
return true;
|
||
|
||
let recalcResult = this.RecalculateKeepNext(CurPage);
|
||
if (recalcresult_NextElement !== recalcResult)
|
||
{
|
||
PRS.RecalcResult = recalcResult;
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculatePageXY = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// Если это первая страница параграфа (CurPage = 0), тогда мы должны использовать координаты, которые нам
|
||
// были заданы сверху, а если не первая, тогда координаты мы должны запросить у родительского класса.
|
||
// TODO: Тут отдельно обрабатывается случай, когда рамка переносится на новую страницу, т.е. страница начинается
|
||
// сразу с рамки. Надо бы не разбивать в данной ситуации рамку на страницы, а просто новую страницу начать
|
||
// с нее на уровне DocumentContent.
|
||
|
||
var XStart, YStart, XLimit, YLimit, oFramePr;
|
||
if (0 === CurPage || ((oFramePr = this.Get_FramePr()) && !oFramePr.IsInline() && this.LogicDocument === this.Parent))
|
||
{
|
||
XStart = this.X;
|
||
YStart = this.Y;
|
||
XLimit = this.XLimit;
|
||
YLimit = this.YLimit;
|
||
}
|
||
else
|
||
{
|
||
var PageStart = this.GetPageContentFrame(CurPage);
|
||
|
||
XStart = PageStart.X;
|
||
YStart = PageStart.Y;
|
||
XLimit = PageStart.XLimit;
|
||
YLimit = PageStart.YLimit;
|
||
}
|
||
|
||
PRS.XStart = XStart;
|
||
PRS.YStart = YStart;
|
||
PRS.XLimit = XLimit;// - ParaPr.Ind.Right;
|
||
PRS.YLimit = YLimit;
|
||
PRS.Y = YStart;
|
||
|
||
this.Pages.length = CurPage + 1;
|
||
this.Pages[CurPage] = new CParaPage(XStart, YStart, XLimit, YLimit, CurLine);
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculatePageBreak = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// Для пустых параграфов с разрывом секции не делаем переноса страницы
|
||
if (undefined !== this.Get_SectionPr() && true === this.IsEmpty())
|
||
return true;
|
||
|
||
let logicDocument = this.GetLogicDocument();
|
||
|
||
var isParentDocument = this.Parent instanceof CDocument;
|
||
var isParentBlockSdt = this.Parent instanceof CDocumentContent
|
||
&& this.Parent.Parent instanceof CBlockLevelSdt
|
||
&& PRS.GetTopDocument() instanceof CDocument
|
||
&& !PRS.IsInTable();
|
||
|
||
if (isParentDocument || isParentBlockSdt)
|
||
{
|
||
// Начинаем параграф с новой страницы
|
||
var PageRelative = this.GetRelativePage(CurPage) - this.GetRelativeStartPage();
|
||
if (0 === PageRelative && true === ParaPr.PageBreakBefore)
|
||
{
|
||
// Если это первый элемент документа или секции, тогда не надо начинать его с новой страницы.
|
||
// Кроме случая, когда у нас разрыв секции на текущей странице. Также не добавляем разрыв страницы для
|
||
// особого пустого параграфа с разрывом секции.
|
||
|
||
var bNeedPageBreak = true;
|
||
|
||
var Prev = this.Get_DocumentPrev();
|
||
if (!Prev && isParentBlockSdt)
|
||
{
|
||
var oSdt = this.Parent.Parent;
|
||
while (oSdt instanceof CBlockLevelSdt)
|
||
{
|
||
Prev = oSdt.Get_DocumentPrev();
|
||
if (Prev)
|
||
break;
|
||
|
||
if (oSdt.Parent instanceof CDocumentContent && oSdt.Parent.Parent instanceof CBlockLevelSdt)
|
||
oSdt = oSdt.Parent.Parent;
|
||
else
|
||
oSdt = null;
|
||
}
|
||
}
|
||
|
||
while (Prev && (Prev instanceof CBlockLevelSdt))
|
||
Prev = Prev.GetLastElement();
|
||
|
||
if ((true === this.IsEmpty() && undefined !== this.Get_SectionPr()) || null === Prev)
|
||
{
|
||
bNeedPageBreak = false;
|
||
}
|
||
else if (this.Parent === this.LogicDocument && type_Paragraph === Prev.GetType() && undefined !== Prev.Get_SectionPr())
|
||
{
|
||
let prevSectPr = Prev.Get_SectionPr();
|
||
let curSectPr = logicDocument.GetSections().GetNextSectPr(prevSectPr);
|
||
if (c_oAscSectionBreakType.Continuous !== curSectPr.Get_Type() || true !== curSectPr.Compare_PageSize(prevSectPr))
|
||
bNeedPageBreak = false;
|
||
}
|
||
|
||
if (true === bNeedPageBreak)
|
||
{
|
||
// Добавляем разрыв страницы
|
||
this.Pages[CurPage].Set_EndLine(CurLine - 1);
|
||
|
||
if (0 === CurLine)
|
||
this.Lines[-1] = new CParaLine();
|
||
|
||
PRS.RecalcResult = recalcresult_NextPage | recalcresultflags_Column;
|
||
return false;
|
||
}
|
||
}
|
||
else if (isParentDocument && true === this.Parent.RecalcInfo.Check_KeepNext(this) && 0 === CurPage && null != this.Get_DocumentPrev())
|
||
{
|
||
this.Pages[CurPage].Set_EndLine( CurLine - 1 );
|
||
if ( 0 === CurLine )
|
||
this.Lines[-1] = new CParaLine( 0 );
|
||
|
||
PRS.RecalcResult = recalcresult_NextPage;
|
||
return false;
|
||
}
|
||
else if (true === this.Is_Inline()) // Случай Flow разбирается в Document.js
|
||
{
|
||
// Проверяем PageBreak и ColumnBreak в предыдущей строке
|
||
var isPageBreakOnPrevLine = false;
|
||
var isColumnBreakOnPrevLine = false;
|
||
|
||
var PrevElement = this.Get_DocumentPrev();
|
||
if (!PrevElement && isParentBlockSdt)
|
||
{
|
||
var oSdt = this.Parent.Parent;
|
||
while (oSdt instanceof CBlockLevelSdt)
|
||
{
|
||
PrevElement = oSdt.Get_DocumentPrev();
|
||
if (PrevElement)
|
||
break;
|
||
|
||
if (oSdt.Parent instanceof CDocumentContent && oSdt.Parent.Parent instanceof CBlockLevelSdt)
|
||
oSdt = oSdt.Parent.Parent;
|
||
else
|
||
oSdt = null;
|
||
}
|
||
}
|
||
|
||
while (PrevElement && (PrevElement instanceof CBlockLevelSdt))
|
||
PrevElement = PrevElement.GetLastElement();
|
||
|
||
var oFootnotes = this.LogicDocument ? this.LogicDocument.Footnotes : null;
|
||
if (null !== PrevElement && type_Paragraph === PrevElement.Get_Type() && true === PrevElement.Is_Empty() && undefined !== PrevElement.Get_SectionPr())
|
||
{
|
||
let prevSectPr = PrevElement.Get_SectionPr();
|
||
let curSectPr = logicDocument.GetSections().GetNextSectPr(prevSectPr);
|
||
if (c_oAscSectionBreakType.Continuous === curSectPr.Get_Type() && true === curSectPr.Compare_PageSize(prevSectPr) && oFootnotes && oFootnotes.IsEmptyPage(PrevElement.GetAbsolutePage(PrevElement.GetPagesCount() - 1)))
|
||
PrevElement = PrevElement.Get_DocumentPrev();
|
||
}
|
||
|
||
if (0 !== CurPage && true !== this.Check_EmptyPages(CurPage - 1))
|
||
{
|
||
var EndLine = this.Pages[CurPage - 1].EndLine;
|
||
if (-1 !== EndLine && this.Lines[EndLine].Info & paralineinfo_BreakRealPage)
|
||
isPageBreakOnPrevLine = true;
|
||
}
|
||
else if (null !== PrevElement && type_Paragraph === PrevElement.Get_Type())
|
||
{
|
||
var bNeedPageBreak = true;
|
||
if (type_Paragraph === PrevElement.GetType() && undefined !== PrevElement.Get_SectionPr())
|
||
{
|
||
let prevSectPr = PrevElement.Get_SectionPr();
|
||
let curSectPr = logicDocument.GetSections().GetNextSectPr(prevSectPr);
|
||
if (c_oAscSectionBreakType.Continuous !== curSectPr.Get_Type() || true !== curSectPr.Compare_PageSize(prevSectPr) || (oFootnotes && !oFootnotes.IsEmptyPage(PrevElement.GetAbsolutePage(PrevElement.GetPagesCount() - 1))))
|
||
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;
|
||
}
|
||
|
||
// Здесь используем GetAbsoluteColumn, а не у текущего класса, т.к. в данную ветку мы попадаем только, если
|
||
// верхний DocContent - документ и если не в таблице
|
||
if ((true === isPageBreakOnPrevLine && (0 !== this.GetAbsoluteColumn(CurPage) || (0 === CurPage && null !== PrevElement)))
|
||
|| (true === isColumnBreakOnPrevLine && 0 === CurPage))
|
||
{
|
||
this.Pages[CurPage].Set_EndLine(CurLine - 1);
|
||
|
||
if (0 === CurLine)
|
||
this.Lines[-1] = new CParaLine();
|
||
|
||
PRS.RecalcResult = recalcresult_NextPage | recalcresultflags_Column;
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLine = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// При пересчете любой строки обновляем эти поля
|
||
this.ParaEnd.Line = -1;
|
||
this.ParaEnd.Range = -1;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 1. Добавляем новую строку в параграф
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.Lines.length = CurLine + 1;
|
||
this.Lines[CurLine] = new CParaLine();
|
||
this.Lines[CurLine].CF = PRS.ComplexFields.getState();
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 2. Проверяем, является ли данная строка висячей
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineWidow(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 3. Заполняем строку отрезками обтекания
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLineFillRanges(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 4. Пересчитываем отрезки данной строки
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineRanges(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 5. Заполняем информацию о строке
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLineInfo(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 6. Пересчитываем метрики данной строки
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLineMetrics(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 7. Рассчитываем высоту строки, а также положение верхней и нижней границ
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLinePosition(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 8. Проверяем достигла ли данная строка конца страницы
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineBottomBound(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 9. Проверяем обтекание данной строки относительно плавающих объектов
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineCheckRanges(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 10. Выставляем вертикальное смещение данной строки
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLineBaseLine(CurLine, CurPage, PRS, ParaPr);
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 11. Проверяем не съехала ли вся строка из-за обтекания
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineCheckRangeY(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 12. Пересчитываем сдвиги элементов внутри параграфа и видимые ширины пробелов, в зависимости от align.
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (!(this.private_RecalculateLineAlign(CurLine, CurPage, PRS, ParaPr, false) & recalcresult_NextElement))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 13. Последние проверки
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineEnd(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 14. Проверяем Последние проверки
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
if (false === this.private_RecalculateLineCheckFootnotes(CurLine, CurPage, PRS, ParaPr))
|
||
return;
|
||
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// 15. Регистрируем концевые сноски на странице
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
this.private_RecalculateLineCheckEndnotes(CurLine, CurPage, PRS, ParaPr);
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineWidow = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// Висячие строки обрабатываются только внутри основного документа
|
||
if ( this.Parent instanceof CDocument && true === this.Parent.RecalcInfo.Check_WidowControl(this, CurLine) )
|
||
{
|
||
this.Parent.RecalcInfo.Need_ResetWidowControl();
|
||
|
||
this.Pages[CurPage].Set_EndLine(CurLine - 1);
|
||
if (0 === CurLine)
|
||
{
|
||
this.Lines[-1] = new CParaLine(0);
|
||
}
|
||
|
||
PRS.RecalcResult = recalcresult_NextPage | recalcresultflags_Column;
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineFillRanges = function(CurLine, CurPage, PRS, paraPr)
|
||
{
|
||
this.Lines[CurLine].Info = 0;
|
||
|
||
// Параметры Ranges и RangesCount не обнуляются здесь, они задаются выше
|
||
var Ranges = PRS.Ranges;
|
||
var RangesCount = PRS.RangesCount;
|
||
|
||
// Обнуляем параметры PRS для строки
|
||
PRS.Reset_Line();
|
||
|
||
// Проверим, нужно ли в данной строке учитывать FirstLine (т.к. не всегда это первая строка должна быть)
|
||
var UseFirstLine = true;
|
||
for ( var TempCurLine = CurLine - 1; TempCurLine >= 0; TempCurLine-- )
|
||
{
|
||
var TempInfo = this.Lines[TempCurLine].Info;
|
||
if (!(TempInfo & paralineinfo_BreakPage) || !(TempInfo & paralineinfo_Empty))
|
||
{
|
||
UseFirstLine = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Проверим неинлайн формулу в первой строке
|
||
if (0 === CurLine && true === UseFirstLine)
|
||
{
|
||
var CurPos = 0;
|
||
var Count = this.Content.length;
|
||
while (CurPos < Count)
|
||
{
|
||
if (true === this.CheckMathPara(CurPos))
|
||
{
|
||
UseFirstLine = false;
|
||
break;
|
||
}
|
||
else if (true !== this.Content[CurPos].Is_Empty())
|
||
{
|
||
break;
|
||
}
|
||
|
||
CurPos++;
|
||
}
|
||
}
|
||
|
||
PRS.UseFirstLine = UseFirstLine;
|
||
|
||
// Заполняем строку отрезками обтекания. Выставляем начальные сдвиги для отрезков. Начало промежутка = конец вырезаемого промежутка
|
||
this.Lines[CurLine].Reset();
|
||
if (paraPr.Bidi)
|
||
{
|
||
let xStart = PRS.XStart + paraPr.Ind.Right;
|
||
|
||
let x0 = RangesCount > 0 ? Ranges[Ranges.length - 1].X1 : xStart;
|
||
let x1 = (UseFirstLine ? PRS.XLimit - paraPr.Ind.Left - paraPr.Ind.FirstLine : PRS.XLimit - paraPr.Ind.Left);
|
||
this.Lines[CurLine].addRange(x0, x1);
|
||
for (let rangeIndex = Ranges.length - 1; rangeIndex >= 0; --rangeIndex)
|
||
{
|
||
x0 = rangeIndex > 0 ? Ranges[rangeIndex - 1].X1 : xStart;
|
||
x1 = Ranges[rangeIndex].X0;
|
||
this.Lines[CurLine].addRange(x0, x1);
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
let xLimit = PRS.XLimit - paraPr.Ind.Right;
|
||
|
||
let x0 = (UseFirstLine ? PRS.XStart + paraPr.Ind.Left + paraPr.Ind.FirstLine : PRS.XStart + paraPr.Ind.Left);
|
||
let x1 = RangesCount > 0 ? Ranges[0].X0 : xLimit;
|
||
this.Lines[CurLine].addRange(x0, x1);
|
||
for (let rangeIndex = 1, rangeCount = Ranges.length; rangeIndex <= rangeCount; ++rangeIndex)
|
||
{
|
||
x0 = Ranges[rangeIndex - 1].X1
|
||
x1 = rangeIndex === RangesCount ? xLimit : Ranges[rangeIndex].X0;
|
||
this.Lines[CurLine].addRange(x0, x1);
|
||
}
|
||
}
|
||
|
||
if (true === PRS.RangeY)
|
||
{
|
||
PRS.RangeY = false;
|
||
this.Lines[CurLine].Info |= paralineinfo_RangeY;
|
||
}
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineRanges = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
var RangesCount = PRS.RangesCount;
|
||
var CurRange = 0;
|
||
while ( CurRange <= RangesCount )
|
||
{
|
||
PRS.Range = CurRange;
|
||
this.private_RecalculateRange(CurRange, CurLine, CurPage, RangesCount, PRS, ParaPr);
|
||
|
||
if (PRS.isForceLineBreak())
|
||
{
|
||
// Поскольку мы выходим досрочно из цикла, нам надо удалить лишние отрезки обтекания
|
||
this.Lines[CurLine].Ranges.length = CurRange + 1;
|
||
break;
|
||
}
|
||
|
||
if ( -1 === this.ParaEnd.Line && true === PRS.End )
|
||
{
|
||
this.ParaEnd.Line = CurLine;
|
||
this.ParaEnd.Range = CurRange;
|
||
}
|
||
|
||
// Такое может случиться, если мы насильно переносим автофигуру на следующую страницу
|
||
if (PRS.RecalcResult & recalcresult_NextPage || PRS.RecalcResult & recalcresult_ParaMath || PRS.RecalcResult & recalcresult_CurLine || PRS.RecalcResult & recalcresult_CurPagePara)
|
||
return false;
|
||
|
||
CurRange++;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineInfo = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
if (true === PRS.BreakPageLine)
|
||
this.Lines[CurLine].Info |= paralineinfo_BreakPage;
|
||
|
||
if (true === PRS.BreakRealPageLine)
|
||
this.Lines[CurLine].Info |= paralineinfo_BreakRealPage;
|
||
|
||
if (true === PRS.EmptyLine)
|
||
this.Lines[CurLine].Info |= paralineinfo_Empty;
|
||
|
||
if (true === PRS.End)
|
||
this.Lines[CurLine].Info |= paralineinfo_End;
|
||
|
||
if (true === PRS.BadLeftTab)
|
||
this.Lines[CurLine].Info |= paralineinfo_BadLeftTab;
|
||
|
||
if (PRS.GetFootnoteReferencesCount(null, true) > 0 || PRS.GetEndnoteReferenceCount() > 0)
|
||
this.Lines[CurLine].Info |= paralineinfo_Notes;
|
||
|
||
if (true === PRS.TextOnLine)
|
||
this.Lines[CurLine].Info |= paralineinfo_TextOnLine;
|
||
|
||
if (true === PRS.BreakLine)
|
||
this.Lines[CurLine].Info |= paralineinfo_BreakLine;
|
||
|
||
if (PRS.LongWord)
|
||
this.Lines[CurLine].Info |= paralineinfo_LongWord;
|
||
|
||
if (PRS.LastHyphenItem)
|
||
this.Lines[CurLine].Info |= paralineinfo_AutoHyphen;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineMetrics = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
var Line = this.Lines[CurLine];
|
||
var RangesCount = Line.Ranges.length;
|
||
|
||
for (var CurRange = 0; CurRange < RangesCount; CurRange++)
|
||
{
|
||
var Range = Line.Ranges[CurRange];
|
||
|
||
var StartPos = Range.StartPos;
|
||
var EndPos = Range.EndPos;
|
||
|
||
for (var Pos = StartPos; Pos <= EndPos; Pos++)
|
||
{
|
||
this.Content[Pos].Recalculate_LineMetrics(PRS, ParaPr, CurLine, CurRange);
|
||
}
|
||
}
|
||
|
||
// Строка пустая, у нее надо выставить ненулевую высоту. Делаем как Word, выставляем высоту по размеру
|
||
// текста, на котором закончилась данная строка.
|
||
if ( true === PRS.EmptyLine || (PRS.LineAscent < 0.001 && PRS.LineDescent < 0.001) || (true === PRS.End && true !== PRS.TextOnLine))
|
||
{
|
||
let useParaEnd = true;
|
||
if (true !== PRS.End)
|
||
{
|
||
let lastRange = this.Lines[CurLine].Ranges.length - 1;
|
||
let lastItem = this.Content[this.Lines[CurLine].Ranges[lastRange].EndPos];
|
||
let lastRun = lastItem.Get_LastRunInRange(CurLine, lastRange);
|
||
if (lastRun && lastRun instanceof AscWord.CRun)
|
||
{
|
||
let metrics = lastRun.getTextMetrics(true);
|
||
|
||
let textDescent = metrics.Descent;
|
||
let textAscent = metrics.Ascent + metrics.LineGap;
|
||
let textAscent2 = metrics.Ascent;
|
||
|
||
if (PRS.LineTextAscent < textAscent)
|
||
PRS.LineTextAscent = textAscent;
|
||
|
||
if (PRS.LineTextAscent2 < textAscent2)
|
||
PRS.LineTextAscent2 = textAscent2;
|
||
|
||
if (PRS.LineTextDescent < textDescent)
|
||
PRS.LineTextDescent = textDescent;
|
||
|
||
if (PRS.LineAscent < textAscent)
|
||
PRS.LineAscent = textAscent;
|
||
|
||
if (PRS.LineDescent < textDescent)
|
||
PRS.LineDescent = textDescent;
|
||
|
||
useParaEnd = false;
|
||
}
|
||
}
|
||
|
||
if (useParaEnd || (PRS.LineAscent < 0.001 && PRS.LineDescent < 0.001))
|
||
{
|
||
let oTextPr = this.GetParaEndCompiledPr();
|
||
let oMetrics = oTextPr.GetTextMetrics(oTextPr.CS || oTextPr.RTL ? AscWord.fontslot_CS : AscWord.fontslot_ASCII, this.GetTheme());
|
||
|
||
let EndTextDescent = oMetrics.Descent;
|
||
let EndTextAscent = oMetrics.Ascent + oMetrics.LineGap;
|
||
let EndTextAscent2 = oMetrics.Ascent;
|
||
|
||
PRS.LineTextAscent = EndTextAscent;
|
||
PRS.LineTextAscent2 = EndTextAscent2;
|
||
PRS.LineTextDescent = EndTextDescent;
|
||
|
||
if (PRS.LineAscent < EndTextAscent)
|
||
PRS.LineAscent = EndTextAscent;
|
||
|
||
if (PRS.LineDescent < EndTextDescent)
|
||
PRS.LineDescent = EndTextDescent;
|
||
}
|
||
}
|
||
|
||
// Рассчитаем метрики строки
|
||
this.Lines[CurLine].Metrics.Update( PRS.LineTextAscent, PRS.LineTextAscent2, PRS.LineTextDescent, PRS.LineAscent, PRS.LineDescent, ParaPr );
|
||
|
||
if (true === PRS.End && true !== PRS.EmptyLine && true !== PRS.TextOnLine && Math.abs(this.Lines[CurLine].Metrics.Descent - this.Lines[CurLine].Metrics.TextDescent) < 0.001)
|
||
this.Lines[CurLine].Metrics.Descent = 0;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLinePosition = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// Важно: Значение Border.Space учитывается всегда, даже когда Border.Value = none, а
|
||
// вот Border.Size зависит уже от Border.Value
|
||
|
||
var BaseLineOffset = 0;
|
||
if (CurLine === this.Pages[CurPage].FirstLine)
|
||
{
|
||
var oForm = null;
|
||
if (this.IsInFixedForm() && (oForm = this.GetInnerForm()) && oForm.IsMultiLineForm() && oForm.IsTextForm())
|
||
{
|
||
var oRun = oForm.GetElement(0);
|
||
if (oRun && oRun instanceof ParaRun)
|
||
{
|
||
// Адобовский вариант отступа первой строки для многострочных форм
|
||
var oTextPr = oRun.Get_CompiledTextPr(false);
|
||
g_oTextMeasurer.SetTextPr(oTextPr, this.GetTheme());
|
||
g_oTextMeasurer.SetFontSlot(AscWord.fontslot_ASCII, 1);
|
||
var oLimits = g_oTextMeasurer.GetLimitsY();
|
||
var nBBoxH = oLimits.max - oLimits.min + 2 * 25.4 / 72;
|
||
|
||
if (this.Lines[CurLine].Metrics.Ascent < nBBoxH)
|
||
this.Lines[CurLine].Metrics.Ascent = nBBoxH;
|
||
}
|
||
}
|
||
|
||
BaseLineOffset = this.Lines[CurLine].Metrics.Ascent;
|
||
|
||
if (this.Check_FirstPage(CurPage, true))
|
||
{
|
||
// Добавляем расстояние до параграфа (Pr.Spacing.Before)
|
||
if (this.private_CheckNeedBeforeSpacing(CurPage, PRS.Parent, PRS.GetPageAbs(), ParaPr))
|
||
BaseLineOffset += ParaPr.Spacing.Before;
|
||
|
||
// Добавляем толщину границы параграфа (если граница задана)
|
||
if ((true === ParaPr.Brd.First || 1 === CurPage))
|
||
{
|
||
BaseLineOffset += ParaPr.Brd.Top.Space;
|
||
if (border_Single === ParaPr.Brd.Top.Value)
|
||
BaseLineOffset += ParaPr.Brd.Top.Size;
|
||
}
|
||
else if (false === ParaPr.Brd.First)
|
||
{
|
||
BaseLineOffset += ParaPr.Brd.Between.Space;
|
||
if (border_Single === ParaPr.Brd.Between.Value)
|
||
BaseLineOffset += ParaPr.Brd.Between.Size;
|
||
}
|
||
}
|
||
|
||
PRS.BaseLineOffset = BaseLineOffset;
|
||
}
|
||
else
|
||
{
|
||
if (this.Lines[CurLine].Info & paralineinfo_RangeY)
|
||
PRS.BaseLineOffset = this.Lines[CurLine].Metrics.Ascent;
|
||
else
|
||
BaseLineOffset = PRS.BaseLineOffset;
|
||
}
|
||
|
||
var Top, Bottom;
|
||
var Top2, Bottom2; // верх и низ без Pr.Spacing
|
||
|
||
var PrevBottom = this.Pages[CurPage].Bounds.Bottom;
|
||
|
||
if (this.Lines[CurLine].Info & paralineinfo_RangeY)
|
||
{
|
||
Top = PRS.Y;
|
||
Top2 = PRS.Y;
|
||
|
||
if (CurLine === this.Pages[CurPage].FirstLine && this.Check_FirstPage(CurPage, true))
|
||
{
|
||
if (this.private_CheckNeedBeforeSpacing(CurPage, PRS.Parent, PRS.GetPageAbs(), ParaPr))
|
||
{
|
||
Top2 = Top + ParaPr.Spacing.Before;
|
||
Bottom2 = Top + ParaPr.Spacing.Before + this.Lines[0].Metrics.Ascent + this.Lines[0].Metrics.Descent;
|
||
|
||
if (true === ParaPr.Brd.First)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Space;
|
||
Bottom2 += ParaPr.Brd.Top.Space;
|
||
if (border_Single === ParaPr.Brd.Top.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Size;
|
||
Bottom2 += ParaPr.Brd.Top.Size;
|
||
}
|
||
}
|
||
else if (false === ParaPr.Brd.First)
|
||
{
|
||
Top2 += ParaPr.Brd.Between.Space;
|
||
Bottom2 += ParaPr.Brd.Between.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Between.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Between.Size;
|
||
Bottom2 += ParaPr.Brd.Between.Size;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Параграф начинается с новой страницы
|
||
Bottom2 = Top + this.Lines[0].Metrics.Ascent + this.Lines[0].Metrics.Descent;
|
||
|
||
Top2 += ParaPr.Brd.Top.Space;
|
||
Bottom2 += ParaPr.Brd.Top.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Top.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Size;
|
||
Bottom2 += ParaPr.Brd.Top.Size;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Bottom2 = Top + this.Lines[CurLine].Metrics.Ascent + this.Lines[CurLine].Metrics.Descent;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (CurLine !== this.Pages[CurPage].FirstLine || !this.Check_FirstPage(CurPage, true))
|
||
{
|
||
if (CurLine !== this.Pages[CurPage].FirstLine)
|
||
{
|
||
Top = PRS.Y + BaseLineOffset + this.Lines[CurLine - 1].Metrics.Descent + this.Lines[CurLine - 1].Metrics.LineGap;
|
||
Top2 = Top;
|
||
Bottom2 = Top + this.Lines[CurLine].Metrics.Ascent + this.Lines[CurLine].Metrics.Descent;
|
||
}
|
||
else
|
||
{
|
||
Top = this.Pages[CurPage].Y;
|
||
Top2 = Top;
|
||
Bottom2 = Top + this.Lines[CurLine].Metrics.Ascent + this.Lines[CurLine].Metrics.Descent;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Top = PRS.Y;
|
||
Top2 = PRS.Y;
|
||
|
||
if (this.private_CheckNeedBeforeSpacing(CurPage, PRS.Parent, PRS.GetPageAbs(), ParaPr))
|
||
{
|
||
Top2 = Top + ParaPr.Spacing.Before;
|
||
Bottom2 = Top + ParaPr.Spacing.Before + this.Lines[CurLine].Metrics.Ascent + this.Lines[CurLine].Metrics.Descent;
|
||
|
||
if (true === ParaPr.Brd.First)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Space;
|
||
Bottom2 += ParaPr.Brd.Top.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Top.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Size;
|
||
Bottom2 += ParaPr.Brd.Top.Size;
|
||
}
|
||
}
|
||
else if (false === ParaPr.Brd.First)
|
||
{
|
||
Top2 += ParaPr.Brd.Between.Space;
|
||
Bottom2 += ParaPr.Brd.Between.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Between.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Between.Size;
|
||
Bottom2 += ParaPr.Brd.Between.Size;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Параграф начинается с новой страницы
|
||
Bottom2 = Top + this.Lines[CurLine].Metrics.Ascent + this.Lines[CurLine].Metrics.Descent;
|
||
|
||
Top2 += ParaPr.Brd.Top.Space;
|
||
Bottom2 += ParaPr.Brd.Top.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Top.Value)
|
||
{
|
||
Top2 += ParaPr.Brd.Top.Size;
|
||
Bottom2 += ParaPr.Brd.Top.Size;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Bottom = Bottom2;
|
||
Bottom += this.Lines[CurLine].Metrics.LineGap;
|
||
|
||
if (this.Lines[CurLine].Metrics.LineGap < 0)
|
||
Bottom2 += this.Lines[CurLine].Metrics.LineGap;
|
||
|
||
// Если данная строка последняя, тогда подкорректируем нижнюю границу
|
||
if ( true === PRS.End )
|
||
{
|
||
Bottom += ParaPr.Spacing.After;
|
||
|
||
// Если нижняя граница Between, тогда она учитывается в следующем параграфе
|
||
if (true === ParaPr.Brd.Last)
|
||
{
|
||
Bottom += ParaPr.Brd.Bottom.Space;
|
||
|
||
if (border_Single === ParaPr.Brd.Bottom.Value)
|
||
Bottom += ParaPr.Brd.Bottom.Size;
|
||
}
|
||
else
|
||
{
|
||
Bottom += ParaPr.Brd.Between.Space;
|
||
}
|
||
|
||
// TODO: Здесь нужно сделать корректировку YLimit с учетом сносок. Надо разобраться почему вообще здесь
|
||
// используется this.YLimit вместо Page.YLimit
|
||
|
||
if (!this.Parent.IsCalculatingContinuousSectionBottomLine() && false === this.IsTableCellContent() && Bottom > this.YLimit && Bottom - this.YLimit <= ParaPr.Spacing.After)
|
||
Bottom = this.YLimit;
|
||
}
|
||
|
||
this.Lines[CurLine].Top = Top - this.Pages[CurPage].Y;
|
||
this.Lines[CurLine].Bottom = Bottom - this.Pages[CurPage].Y;
|
||
|
||
// В MSWord версиях 14 и ниже пустая строка с переносом колонки не имеет высоты
|
||
// Заметим, что границы строки мы оставляем корректными
|
||
if (PRS.getCompatibilityMode() <= AscCommon.document_compatibility_mode_Word14
|
||
&& this.Lines[CurLine].Info & paralineinfo_BreakPage
|
||
&& this.Lines[CurLine].Info & paralineinfo_Empty
|
||
&& !(this.Lines[CurLine].Info & paralineinfo_BreakRealPage))
|
||
{
|
||
Bottom = Top;
|
||
Top2 = Top;
|
||
Bottom2 = Top;
|
||
}
|
||
|
||
// Верхнюю границу мы сохраняем только для первой строки данной страницы
|
||
if (CurLine === this.Pages[CurPage].FirstLine && !(this.Lines[CurLine].Info & paralineinfo_RangeY))
|
||
this.Pages[CurPage].Bounds.Top = Top;
|
||
|
||
this.Pages[CurPage].Bounds.Bottom = Bottom;
|
||
|
||
PRS.LineTop = AscCommon.CorrectMMToTwips(Top);
|
||
PRS.LineBottom = AscCommon.CorrectMMToTwips(Bottom);
|
||
PRS.LineTop2 = AscCommon.CorrectMMToTwips(Top2);
|
||
PRS.LineBottom2 = AscCommon.CorrectMMToTwips(Bottom2);
|
||
PRS.LinePrevBottom = AscCommon.CorrectMMToTwips(PrevBottom);
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineBottomBound = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
var Top = PRS.LineTop;
|
||
var Bottom2 = PRS.LineBottom2;
|
||
|
||
// В ячейке перенос страницы происходит по нижней границе, т.е. с учетом Spacing.After и границы
|
||
if ( true === this.IsTableCellContent() )
|
||
Bottom2 = PRS.LineBottom;
|
||
|
||
// Переносим строку по PageBreak. Если в строке ничего нет кроме PageBreak, и это не конец параграфа, тогда нам не надо проверять высоту строки и обтекание.
|
||
var LineInfo = this.Lines[CurLine].Info;
|
||
var BreakPageLineEmpty = (LineInfo & paralineinfo_BreakPage && LineInfo & paralineinfo_Empty && !(LineInfo & paralineinfo_End) ? true : false);
|
||
PRS.BreakPageLineEmpty = BreakPageLineEmpty;
|
||
|
||
var RealCurPage = this.GetRelativePage(CurPage) - this.GetRelativeStartPage();
|
||
|
||
var YLimit = PRS.YLimit;
|
||
var oTopDocument = PRS.TopDocument;
|
||
var bNoFootnotes = true;
|
||
if (oTopDocument instanceof CDocument)
|
||
{
|
||
// bNoFootnotes - означает есть или нет сноска на данной колонке
|
||
var nHeight = oTopDocument.Footnotes.GetHeight(PRS.PageAbs, PRS.ColumnAbs);
|
||
if (nHeight > 0.001)
|
||
{
|
||
bNoFootnotes = false;
|
||
|
||
// В таблицах граница разруливается по своему
|
||
if (true !== PRS.IsInTable())
|
||
YLimit -= nHeight;
|
||
}
|
||
}
|
||
else if (oTopDocument instanceof CFootEndnote)
|
||
{
|
||
// bNoFootnotes - означает, первая или нет данная сноска в колонке. Если она не первая,
|
||
// тогда если у нее не убирается первая строка первого параграфа, все равно надо делать перенос
|
||
var oController = oTopDocument.GetParent();
|
||
if (oController instanceof CEndnotesController || !oController.IsEmptyPageColumn(PRS.PageAbs, PRS.ColumnAbs, oTopDocument.GetSectionIndex()))
|
||
bNoFootnotes = false;
|
||
}
|
||
|
||
// Сначала проверяем не нужно ли сделать перенос страницы в данном месте
|
||
// Перенос не делаем, если это первая строка на новой странице
|
||
if (true === this.UseLimit()
|
||
&& (Top > YLimit || Bottom2 > YLimit)
|
||
&& (CurLine != this.Pages[CurPage].FirstLine
|
||
|| false === bNoFootnotes
|
||
|| (0 === RealCurPage && ((null != this.Get_DocumentPrev() && !this.Parent.IsElementStartOnNewPage(this.GetIndex()))
|
||
|| (true === this.IsTableCellContent() && true !== this.Parent.IsTableFirstRowOnNewPage())
|
||
|| (true === this.Parent.IsBlockLevelSdtContent() && true !== this.Parent.IsBlockLevelSdtFirstOnNewPage()))))
|
||
&& false === BreakPageLineEmpty)
|
||
{
|
||
this.private_RecalculateMoveLineToNextPage(CurLine, CurPage, PRS, ParaPr);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineCheckRanges = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
var Top = PRS.LineTop;
|
||
var Bottom = PRS.LineBottom;
|
||
var Top2 = PRS.LineTop2;
|
||
var Bottom2 = PRS.LineBottom2;
|
||
|
||
var Left = this.Pages[CurPage].X;
|
||
var Right = this.Pages[CurPage].XLimit;
|
||
if (!PRS.MathNotInline)
|
||
{
|
||
if (ParaPr.Bidi)
|
||
{
|
||
Left += ParaPr.Ind.Right;
|
||
Right -= PRS.UseFirstLine ? ParaPr.Ind.Left + ParaPr.Ind.FirstLine : ParaPr.Ind.Left;
|
||
}
|
||
else
|
||
{
|
||
Right -= ParaPr.Ind.Right;
|
||
Left += PRS.UseFirstLine ? ParaPr.Ind.Left + ParaPr.Ind.FirstLine : ParaPr.Ind.Left;
|
||
}
|
||
}
|
||
|
||
var PageFields = null;
|
||
if (this.bFromDocument && PRS.GetTopDocument() === this.LogicDocument && !PRS.IsInTable())
|
||
{
|
||
// Заглушка для случая, когда параграф лежит в CBlockLevelSdt
|
||
PageFields = this.LogicDocument.GetColumnFields(this.GetAbsolutePage(CurPage), this.GetAbsoluteColumn(CurPage), this.GetAbsoluteSection(CurPage));
|
||
}
|
||
else
|
||
{
|
||
PageFields = this.Parent.GetColumnFields ? this.Parent.GetColumnFields(this.GetAbsolutePage(CurPage), this.GetAbsoluteColumn(CurPage), this.GetAbsoluteSection(CurPage)) : this.Parent.Get_PageFields(this.GetRelativePage(CurPage), this.Parent.IsHdrFtr());
|
||
}
|
||
|
||
var Ranges = PRS.Ranges;
|
||
var Ranges2;
|
||
|
||
for (var nIndex = 0, nCount = Ranges.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
Ranges[nIndex].Y1 = AscCommon.CorrectMMToTwips(Ranges[nIndex].Y1);
|
||
}
|
||
|
||
if (PRS.getCompatibilityMode() >= AscCommon.document_compatibility_mode_Word15)
|
||
{
|
||
Bottom = Bottom2;
|
||
Top2 = Top;
|
||
}
|
||
|
||
if ( true === this.Use_Wrap() )
|
||
Ranges2 = this.Parent.CheckRange(Left, Top, Right, Bottom, Top2, Bottom2, PageFields.X, PageFields.XLimit, this.GetRelativePage(CurPage), true, PRS.MathNotInline);
|
||
else
|
||
Ranges2 = [];
|
||
|
||
// Проверяем совпали ли промежутки. Если совпали, тогда данная строчка рассчитана верно, и мы переходим к
|
||
// следующей, если нет, тогда заново рассчитываем данную строчку, но с новыми промежутками.
|
||
// Заметим, что тут возможен случай, когда Ranges2 меньше, чем Ranges, такое может случится при повторном
|
||
// обсчете строки. (После первого расчета мы выяснили что Ranges < Ranges2, при повторном обсчете строки, т.к.
|
||
// она стала меньше, то у нее и рассчитанная высота могла уменьшиться, а значит Ranges2 могло оказаться
|
||
// меньше чем Ranges). В таком случае не надо делать повторный пересчет, иначе будет зависание.
|
||
if (-1 === FlowObjects_CompareRanges(Ranges, Ranges2) && true === FlowObjects_CheckInjection(Ranges, Ranges2) && false === PRS.BreakPageLineEmpty)
|
||
{
|
||
// Выставляем новые отрезки обтекания и сообщаем, что надо заново персчитать данную строку
|
||
PRS.Ranges = Ranges2;
|
||
PRS.RangesCount = Ranges2.length;
|
||
PRS.RecalcResult = recalcresult_CurLine;
|
||
|
||
if (this.Lines[CurLine].Info & paralineinfo_RangeY)
|
||
PRS.RangeY = true;
|
||
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineBaseLine = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
if (this.Lines[CurLine].Info & paralineinfo_RangeY)
|
||
{
|
||
this.Lines[CurLine].Y = PRS.Y - this.Pages[CurPage].Y;
|
||
}
|
||
else
|
||
{
|
||
if (CurLine > 0)
|
||
{
|
||
// Первая линия на странице не должна двигаться
|
||
if (CurLine != this.Pages[CurPage].FirstLine && ( true === PRS.End || true !== PRS.EmptyLine || PRS.RangesCount <= 0 || true === PRS.NewPage ))
|
||
PRS.Y += this.Lines[CurLine - 1].Metrics.Descent + this.Lines[CurLine - 1].Metrics.LineGap + this.Lines[CurLine].Metrics.Ascent;
|
||
|
||
this.Lines[CurLine].Y = PRS.Y - this.Pages[CurPage].Y;
|
||
}
|
||
else
|
||
this.Lines[0].Y = 0;
|
||
}
|
||
|
||
this.Lines[CurLine].Y += PRS.BaseLineOffset;
|
||
if (this.Lines[CurLine].Metrics.LineGap < 0)
|
||
this.Lines[CurLine].Y += this.Lines[CurLine].Metrics.LineGap;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineCheckRangeY = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// Такое случается, когда у нас после пересчета Flow картинки, место к которому она была привязана перешло на
|
||
// следующую страницу.
|
||
if (PRS.RecalcResult & recalcresult_NextPage)
|
||
return false;
|
||
|
||
// Если строка пустая в следствии того, что у нас было обтекание, тогда мы не добавляем новую строку,
|
||
// а просто текущую смещаем ниже.
|
||
|
||
if (true === PRS.EmptyLine && true === PRS.bMathRangeY) // нужный PRS.Y выставляется в ParaMath
|
||
{
|
||
PRS.bMathRangeY = false;
|
||
|
||
// Отмечаем, что данная строка переносится по Y из-за обтекания
|
||
PRS.RangeY = true;
|
||
|
||
// Пересчитываем заново данную строку
|
||
PRS.Reset_Ranges();
|
||
PRS.RecalcResult = recalcresult_CurLine;
|
||
|
||
return false;
|
||
}
|
||
else if (true !== PRS.End && true === PRS.EmptyLine && PRS.RangesCount > 0)
|
||
{
|
||
// Найдем верхнюю точку объектов обтекания (т.е. так чтобы при новом обсчете не учитывался только
|
||
// этот объект, заканчивающийся выше всех)
|
||
|
||
var Ranges = PRS.Ranges;
|
||
|
||
var RangesMaxY = Ranges[0].Y1;
|
||
for (var Index = 1; Index < Ranges.length; Index++)
|
||
{
|
||
if (RangesMaxY > Ranges[Index].Y1)
|
||
RangesMaxY = Ranges[Index].Y1;
|
||
}
|
||
|
||
if (Math.abs(RangesMaxY - PRS.Y) < 0.001)
|
||
PRS.Y = RangesMaxY + 1; // смещаемся по 1мм
|
||
else
|
||
PRS.Y = RangesMaxY + AscCommon.TwipsToMM(1) + 0.001; // Добавляем 0.001, чтобы избавиться от погрешности
|
||
|
||
// Отмечаем, что данная строка переносится по Y из-за обтекания
|
||
PRS.RangeY = true;
|
||
|
||
// Пересчитываем заново данную строку
|
||
PRS.Reset_Ranges();
|
||
PRS.RecalcResult = recalcresult_CurLine;
|
||
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineEnd = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
if ( true === PRS.NewPage )
|
||
{
|
||
// Если это последний элемент параграфа, тогда нам не надо переносить текущий параграф
|
||
// на новую страницу. Нам надо выставить границы так, чтобы следующий параграф начинался
|
||
// с новой страницы.
|
||
this.Pages[CurPage].Set_EndLine( CurLine );
|
||
PRS.RecalcResult = recalcresult_NextPage;
|
||
return false;
|
||
}
|
||
|
||
if (true !== PRS.End)
|
||
{
|
||
if (PRS.ForceNewPageAfter)
|
||
{
|
||
this.Pages[CurPage].Set_EndLine(CurLine);
|
||
this.Pages[CurPage].Bounds.Bottom = AscWord.MAX_MM_VALUE;
|
||
PRS.RecalcResult = recalcresult_NextPage;
|
||
return false;
|
||
}
|
||
else if (PRS.ForceNewPage)
|
||
{
|
||
this.Pages[CurPage].Set_EndLine( CurLine - 1 );
|
||
|
||
if ( 0 === CurLine )
|
||
this.Lines[-1] = new CParaLine();
|
||
|
||
PRS.RecalcResult = recalcresult_NextPage;
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (PRS.ForceNewPageAfter)
|
||
this.Pages[CurPage].Bounds.Bottom = AscWord.MAX_MM_VALUE;
|
||
|
||
// В последней строке могут быть заполнены не все отрезки обтекания. Удаляем лишние.
|
||
if (PRS.Range < PRS.RangesCount)
|
||
this.Lines[CurLine].Ranges.length = PRS.Range + 1;
|
||
|
||
// Проверим висячую строку
|
||
if (true === ParaPr.WidowControl && CurLine === this.Pages[CurPage].StartLine && CurLine >= 1 && false === this.private_CheckSkipKeepLinesAndWidowControl(CurPage))
|
||
{
|
||
// Проверим не встречается ли в предыдущей строке BreakPage, если да, тогда не учитываем WidowControl
|
||
var BreakPagePrevLine = (this.Lines[CurLine - 1].Info & paralineinfo_BreakPage) | 0;
|
||
|
||
if (this.Parent instanceof CDocument
|
||
&& true === this.Parent.RecalcInfo.Can_RecalcWidowControl()
|
||
&& 0 === BreakPagePrevLine
|
||
&& (1 === CurPage && null != this.Get_DocumentPrev())
|
||
&& this.Lines[CurLine - 1].Ranges.length <= 1)
|
||
{
|
||
var bBreakPageFromStart = false;
|
||
for (var Index = 0, Count = this.Pages[CurPage - 1].Drawings.length; Index < Count; Index++)
|
||
{
|
||
var Drawing = this.Pages[CurPage - 1].Drawings[Index];
|
||
var DrawingLine = Drawing.LineNum;
|
||
|
||
if (DrawingLine >= CurLine - 1)
|
||
{
|
||
bBreakPageFromStart = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Если в строках, которые мы переносим есть картинки, либо, если у нас в параграфе 3 строки,
|
||
// тогда сразу начинаем параграф с новой строки
|
||
if (true === bBreakPageFromStart || CurLine <= 2)
|
||
{
|
||
CurLine = 0;
|
||
// Вызываем данную функцию для удаления картинок с предыдущей страницы
|
||
this.Recalculate_Drawing_AddPageBreak(0, 0, true);
|
||
}
|
||
else
|
||
CurLine = CurLine - 1;
|
||
|
||
this.Parent.RecalcInfo.Set_WidowControl(this, CurLine);
|
||
PRS.RecalcResult = recalcresult_PrevPage | recalcresultflags_Column;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Если у нас нумерация относится к знаку конца параграфа, тогда в такой
|
||
// ситуации не рисуем нумерацию у такого параграфа.
|
||
if (para_End === this.Numbering.Item.Type && this.Lines[CurLine].Info & paralineinfo_BreakPage)
|
||
{
|
||
this.Numbering.Item = null;
|
||
this.Numbering.Run = null;
|
||
this.Numbering.Line = -1;
|
||
this.Numbering.Range = -1;
|
||
}
|
||
|
||
this.Pages[CurPage].Set_EndLine( CurLine );
|
||
PRS.RecalcResult = recalcresult_NextElement;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineAlign = function(CurLine, CurPage, PRS, ParaPr, Fast)
|
||
{
|
||
// Здесь мы пересчитываем ширину пробелов (и в особенных случаях дополнительное
|
||
// расстояние между символами) с учетом прилегания параграфа.
|
||
// 1. Если align = left, тогда внутри каждого промежутка текста выравниваем его
|
||
// к левой границе промежутка.
|
||
// 2. Если align = right, тогда внутри каждого промежутка текста выравниваем его
|
||
// к правой границе промежутка.
|
||
// 3. Если align = center, тогда внутри каждого промежутка текста выравниваем его
|
||
// по центру промежутка.
|
||
// 4. Если align = justify, тогда
|
||
// 4.1 Если внутри промежутка ровно 1 слово.
|
||
// 4.1.1 Если промежуток в строке 1 и слово занимает почти всю строку,
|
||
// добавляем в слове к каждой букве дополнительное расстояние между
|
||
// символами, чтобы ширина слова совпала с шириной строки.
|
||
// 4.1.2 Если промежуток первый, тогда слово приставляем к левой границе
|
||
// промежутка
|
||
// 4.1.3 Если промежуток последний, тогда приставляем слово к правой
|
||
// границе промежутка
|
||
// 4.1.4 Если промежуток ни первый, ни последний, тогда ставим слово по
|
||
// середине промежутка
|
||
// 4.2 Если слов больше 1, тогда, исходя из количества пробелов между словами в
|
||
// промежутке, увеличиваем их на столько, чтобы правая граница последнего
|
||
// слова совпала с правой границей промежутка
|
||
var PRSW = PRS;
|
||
var PRSC = PRS.getCounterState();
|
||
var PRSA = PRS.getAlignState();
|
||
PRSA.Paragraph = this;
|
||
PRSA.LastW = 0;
|
||
PRSA.RecalcFast = Fast;
|
||
PRSA.RecalcResult = recalcresult_NextElement;
|
||
PRSA.PageY = this.Pages[CurPage].Bounds.Top;
|
||
PRSA.PageX = this.Pages[CurPage].Bounds.Left;
|
||
|
||
var Line = this.Lines[CurLine];
|
||
var RangesCount = Line.Ranges.length;
|
||
|
||
var isDoNotExpandShiftReturn = this.LogicDocument ? this.LogicDocument.IsDoNotExpandShiftReturn() : false;
|
||
|
||
for (var CurRange = 0; CurRange < RangesCount; CurRange++)
|
||
{
|
||
var Range = Line.Ranges[CurRange];
|
||
|
||
var StartPos = Range.StartPos;
|
||
var EndPos = Range.EndPos;
|
||
|
||
PRSC.Reset( this, Range );
|
||
|
||
PRSC.Range.W = 0;
|
||
PRSC.Range.WEnd = 0;
|
||
PRSC.Range.WBreak = 0;
|
||
if ( true === this.Numbering.checkRange(CurRange, CurLine) )
|
||
PRSC.Range.W += this.Numbering.WidthVisible;
|
||
|
||
for ( var Pos = StartPos; Pos <= EndPos; Pos++ )
|
||
{
|
||
var Item = this.Content[Pos];
|
||
Item.Recalculate_Range_Width( PRSC, CurLine, CurRange );
|
||
}
|
||
|
||
|
||
var JustifyWord = 0;
|
||
var JustifySpace = 0;
|
||
var RangeWidth = Range.XEnd - Range.X;
|
||
|
||
var X = 0;
|
||
|
||
let rtlShift = PRSC.SpaceLen;
|
||
let bRtlAlign = ParaPr.Bidi;
|
||
let jc = ParaPr.Jc;
|
||
|
||
if (!this.bFromDocument && !PRS.isDocumentEditor() && bRtlAlign)
|
||
{
|
||
if (jc === AscCommon.align_Left)
|
||
jc = AscCommon.align_Right;
|
||
else if (jc === AscCommon.align_Right)
|
||
jc = AscCommon.align_Left;
|
||
}
|
||
|
||
// Если данный отрезок содержит только формулу, тогда прилегание данного отрезка определяется формулой
|
||
var ParaMath = this.Check_Range_OnlyMath(CurRange, CurLine);
|
||
if (null !== ParaMath)
|
||
{
|
||
var Math_X = ( 1 === RangesCount ? this.Pages[CurPage].X + ParaPr.Ind.Left : Range.X );
|
||
var Math_XLimit = ( 1 === RangesCount ? this.Pages[CurPage].XLimit - ParaPr.Ind.Right : Range.XEnd );
|
||
|
||
X = ParaMath.Get_AlignToLine(CurLine, CurRange, PRS.Page, Math_X, Math_XLimit);
|
||
}
|
||
else
|
||
{
|
||
if (this.Lines[CurLine].Info & paralineinfo_BadLeftTab)
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
|
||
JustifyWord = 0;
|
||
JustifySpace = 0;
|
||
}
|
||
else
|
||
{
|
||
// RangeWidth - ширина всего пространства в данном отрезке, а Range.W - ширина занимаемого пространства
|
||
switch (jc)
|
||
{
|
||
case AscCommon.align_Left :
|
||
{
|
||
if (bRtlAlign)
|
||
{
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
if (this.IsUseXLimit())
|
||
X = Math.max(X, Range.X - rtlShift);
|
||
}
|
||
else
|
||
{
|
||
X = Range.X;
|
||
}
|
||
break;
|
||
}
|
||
case AscCommon.align_Right:
|
||
{
|
||
if (bRtlAlign)
|
||
{
|
||
X = Range.X - rtlShift;
|
||
if (this.IsUseXLimit())
|
||
X = Math.max(X, Range.X - rtlShift);
|
||
}
|
||
else
|
||
{
|
||
X = Range.X + RangeWidth - Range.W;
|
||
if (this.IsUseXLimit())
|
||
X = Math.max(X, Range.X);
|
||
}
|
||
|
||
break;
|
||
}
|
||
case AscCommon.align_Center:
|
||
{
|
||
if (bRtlAlign)
|
||
{
|
||
X = Range.X + (RangeWidth - Range.W) / 2 - rtlShift;
|
||
if (this.IsUseXLimit())
|
||
X = Math.max(X, Range.X - rtlShift);
|
||
}
|
||
else
|
||
{
|
||
X = Range.X + (RangeWidth - Range.W) / 2;
|
||
if (this.IsUseXLimit())
|
||
X = Math.max(X, Range.X);
|
||
}
|
||
break;
|
||
}
|
||
case AscCommon.align_Justify:
|
||
{
|
||
if (PRSC.ParaEnd || (PRSC.LineBreak && isDoNotExpandShiftReturn))
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
|
||
JustifyWord = 0;
|
||
JustifySpace = 0;
|
||
}
|
||
else if (1 === PRSC.Words || PRSC.Spaces <= 0)
|
||
{
|
||
// Проверяем по количеству пробелов, т.к., например, в китайском языке пробелов нет, но
|
||
// каждый иероглиф как отдельное слово идет.
|
||
if (1 === RangesCount && !(Line.Info & paralineinfo_End) && !bRtlAlign)
|
||
{
|
||
X = Range.X;
|
||
// Либо слово целиком занимает строку, либо не целиком, но разница очень мала
|
||
// либо это набор китайских иероглифов (PRSC.Words > 1)
|
||
if ((RangeWidth - Range.W <= 0.05 * RangeWidth || PRSC.Words > 1) && PRSC.Letters > 1)
|
||
JustifyWord = (RangeWidth - Range.W) / (PRSC.Letters - 1);
|
||
}
|
||
else if (0 === CurRange || Line.Info & paralineinfo_End)
|
||
{
|
||
// TODO: Здесь нужно улучшить проверку, т.к. отключено выравнивание по центру для всей
|
||
// последней строки, а нужно отключить для последнего отрезка, в котором идет
|
||
// конец параграфа.
|
||
if (bRtlAlign)
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
}
|
||
else if (CurRange === RangesCount - 1)
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X - rtlShift;
|
||
else
|
||
X = Range.X + RangeWidth - Range.W;
|
||
}
|
||
else
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X + (RangeWidth - Range.W) / 2 - rtlShift;
|
||
else
|
||
X = Range.X + (RangeWidth - Range.W) / 2;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// TODO: Переделать проверку последнего отрезка в последней строке (нужно выставлять флаг когда пришел PRS.End в отрезке)
|
||
|
||
// Последний промежуток последней строки не надо растягивать по ширине.
|
||
if (PRSC.Spaces > 0 && (!(Line.Info & paralineinfo_End) || CurRange !== Line.Ranges.length - 1))
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
|
||
JustifySpace = (RangeWidth - Range.W) / PRSC.Spaces;
|
||
}
|
||
else
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
|
||
JustifySpace = 0;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
if (bRtlAlign)
|
||
X = Range.X + RangeWidth - Range.W - rtlShift;
|
||
else
|
||
X = Range.X;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Range.Spaces = PRSC.Spaces + PRSC.SpacesSkip;
|
||
|
||
PRSA.Range = Range;
|
||
PRSA.LeftSpace = X - Range.X;
|
||
PRSA.RTL = bRtlAlign;
|
||
|
||
PRSA.X = X;
|
||
PRSA.Y = this.Pages[CurPage].Y + this.Lines[CurLine].Y;
|
||
PRSA.XEnd = Range.XEnd;
|
||
PRSA.JustifyWord = JustifyWord;
|
||
PRSA.JustifySpace = JustifySpace;
|
||
PRSA.SpacesCounter = PRSC.Spaces;
|
||
PRSA.SpacesSkip = PRSC.SpacesSkip;
|
||
PRSA.LettersSkip = PRSC.LettersSkip;
|
||
PRSA.RecalcResult = recalcresult_NextElement;
|
||
|
||
var _LineMetrics = this.Lines[CurLine].Metrics;
|
||
PRSA.Y0 = (this.Pages[CurPage].Y + this.Lines[CurLine].Y - _LineMetrics.Ascent);
|
||
PRSA.Y1 = (this.Pages[CurPage].Y + this.Lines[CurLine].Y + _LineMetrics.Descent);
|
||
if (_LineMetrics.LineGap < 0)
|
||
PRSA.Y1 += _LineMetrics.LineGap;
|
||
|
||
this.Lines[CurLine].Ranges[CurRange].XVisible = X;
|
||
|
||
if ( 0 === CurRange )
|
||
this.Lines[CurLine].X = X - PRSW.XStart;
|
||
|
||
if ( true === this.Numbering.checkRange(CurRange, CurLine) )
|
||
PRSA.X += this.Numbering.WidthVisible;
|
||
|
||
for ( var Pos = StartPos; Pos <= EndPos; Pos++ )
|
||
{
|
||
var Item = this.Content[Pos];
|
||
Item.Recalculate_Range_Spaces(PRSA, CurLine, CurRange, CurPage);
|
||
|
||
if (!(PRSA.RecalcResult & recalcresult_NextElement))
|
||
{
|
||
PRSW.RecalcResult = PRSA.RecalcResult;
|
||
return PRSA.RecalcResult;
|
||
}
|
||
}
|
||
|
||
Range.XEndVisible = PRSA.X;
|
||
|
||
if (bRtlAlign)
|
||
{
|
||
Range.XVisible -= Range.WBreak + Range.WEnd;
|
||
Range.XEndVisible -= Range.WBreak + Range.WEnd;
|
||
}
|
||
}
|
||
|
||
return PRSA.RecalcResult;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineCheckFootnotes = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
if (!((PRS.RecalcResult & recalcresult_NextElement) || (PRS.RecalcResult & recalcresult_NextLine)))
|
||
return false;
|
||
|
||
if (PRS.Fast)
|
||
return true;
|
||
|
||
var oTopDocument = PRS.TopDocument;
|
||
var arrFootnotes = [];
|
||
var oLineBreakPos = this.GetLineEndPos(CurLine);
|
||
for (var nIndex = 0, nCount = PRS.Footnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
var oFootnote = PRS.Footnotes[nIndex].FootnoteReference.GetFootnote();
|
||
var oPos = PRS.Footnotes[nIndex].Pos;
|
||
|
||
// Проверим позицию
|
||
if (oLineBreakPos.Compare(oPos) <= 0)
|
||
continue;
|
||
|
||
arrFootnotes.push(oFootnote);
|
||
}
|
||
|
||
if (oTopDocument instanceof CDocument)
|
||
{
|
||
if (!oTopDocument.Footnotes.RecalculateFootnotes(PRS.PageAbs, PRS.ColumnAbs, this.Pages[CurPage].Y + this.Lines[CurLine].Bottom, arrFootnotes))
|
||
{
|
||
this.private_RecalculateMoveLineToNextPage(CurLine, CurPage, PRS, ParaPr);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateLineCheckEndnotes = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
if (!((PRS.RecalcResult & recalcresult_NextElement) || (PRS.RecalcResult & recalcresult_NextLine)) || PRS.Fast)
|
||
return;
|
||
|
||
var oTopDocument = PRS.TopDocument;
|
||
var arrEndnotes = [];
|
||
var oLineBreakPos = this.GetLineEndPos(CurLine);
|
||
for (var nIndex = 0, nCount = PRS.Endnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
var oEndnote = PRS.Endnotes[nIndex].EndnoteReference.GetFootnote();
|
||
var oPos = PRS.Endnotes[nIndex].Pos;
|
||
|
||
// Проверим позицию
|
||
if (oLineBreakPos.Compare(oPos) <= 0)
|
||
continue;
|
||
|
||
arrEndnotes.push(oEndnote);
|
||
}
|
||
|
||
if (oTopDocument instanceof CDocument)
|
||
oTopDocument.GetEndnotesController().RegisterEndnotes(PRS.PageAbs, arrEndnotes);
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateRange = function(CurRange, CurLine, CurPage, RangesCount, PRS, paraPr)
|
||
{
|
||
// Найдем начальную позицию данного отрезка
|
||
var StartPos = 0;
|
||
if ( 0 === CurLine && 0 === CurRange )
|
||
StartPos = 0;
|
||
else if ( CurRange > 0 )
|
||
StartPos = this.Lines[CurLine].Ranges[CurRange - 1].EndPos;
|
||
else
|
||
StartPos = this.Lines[CurLine - 1].Ranges[ this.Lines[CurLine - 1].Ranges.length - 1 ].EndPos;
|
||
|
||
var Line = this.Lines[CurLine];
|
||
var Range = Line.Ranges[CurRange];
|
||
|
||
this.Lines[CurLine].setRangeStartPos(CurRange, StartPos);
|
||
|
||
// Correct first line indentation if previous ranges were empty
|
||
if (PRS.UseFirstLine && 0 !== CurRange && PRS.EmptyLine)
|
||
{
|
||
let shift = 0;
|
||
if (PRS.getCompatibilityMode() >= AscCommon.document_compatibility_mode_Word15)
|
||
shift = Math.max(paraPr.Ind.FirstLine, 0);
|
||
else
|
||
shift = paraPr.Ind.FirstLine < -AscWord.EPSILON ? paraPr.Ind.Left + paraPr.Ind.FirstLine : paraPr.Ind.FirstLine;
|
||
|
||
if (paraPr.Bidi)
|
||
Range.XEnd -= shift;
|
||
else
|
||
Range.X += shift;
|
||
}
|
||
|
||
PRS.resetRange(Range);
|
||
|
||
var ContentLen = this.Content.length;
|
||
|
||
var Pos = StartPos;
|
||
for ( ;Pos < ContentLen; Pos++ )
|
||
{
|
||
var Item = this.Content[Pos];
|
||
|
||
if ( para_Math === Item.Type )
|
||
{
|
||
var NotInlineMath = this.CheckMathPara(Pos);
|
||
if (true === NotInlineMath && true !== PRS.EmptyLine)
|
||
{
|
||
PRS.ForceNewLine = true;
|
||
PRS.NewRange = true;
|
||
Pos--;
|
||
break;
|
||
}
|
||
// TODO: Надо бы перенести эту проверку на изменение контента параграфа
|
||
Item.Set_Inline(true !== NotInlineMath);
|
||
}
|
||
|
||
if ( ( 0 === Pos && 0 === CurLine && 0 === CurRange ) || Pos !== StartPos )
|
||
{
|
||
Item.Recalculate_Reset(CurRange, CurLine);
|
||
}
|
||
|
||
PRS.Update_CurPos( Pos, 0 );
|
||
Item.Recalculate_Range( PRS, paraPr, 1 );
|
||
|
||
if ( true === PRS.NewRange )
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( Pos >= ContentLen )
|
||
Pos = ContentLen - 1;
|
||
|
||
if (PRS.RecalcResult & recalcresult_NextLine)
|
||
{
|
||
// У нас отрезок пересчитался нормально и тут возможны 2 варианта :
|
||
// 1. Отрезок закончился в данной позиции
|
||
// 2. Не все убралось в заданный отрезок и перенос нужно поставить в позиции PRS.LineBreakPos
|
||
|
||
if ( true === PRS.MoveToLBP )
|
||
{
|
||
// Отмечаем, что в заданной позиции заканчивается отрезок
|
||
this.private_RecalculateRangeEndPos( PRS, PRS.LineBreakPos, 0 );
|
||
}
|
||
else
|
||
this.Lines[CurLine].setRangeEndPos(CurRange, Pos);
|
||
}
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateRangeEndPos = function(PRS, PRP, Depth)
|
||
{
|
||
var CurLine = PRS.Line;
|
||
var CurRange = PRS.Range;
|
||
var CurPos = PRP.Get(Depth);
|
||
|
||
this.Content[CurPos].Recalculate_Set_RangeEndPos(PRS, PRP, Depth + 1);
|
||
this.Lines[CurLine].setRangeEndPos(CurRange, CurPos);
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateGetTabPos = function(PRS, X, ParaPr, CurPage, NumTab)
|
||
{
|
||
let contentFrame = this.GetPageContentFrame(CurPage);
|
||
|
||
let startX = contentFrame.X;
|
||
let endX = contentFrame.XLimit;
|
||
|
||
let paraFrame = this.Get_FramePr();
|
||
if (paraFrame)
|
||
{
|
||
startX = 0;
|
||
endX = paraFrame.W;
|
||
}
|
||
|
||
if (PRS.RangesCount > 0 && Math.abs(PRS.Ranges[0].X0 - contentFrame.X) < 0.001)
|
||
startX = PRS.Ranges[0].X1;
|
||
|
||
if (this.isRtlDirection())
|
||
{
|
||
let pageRel = this.GetRelativePage(CurPage);
|
||
let pageLimits = this.Parent.Get_PageLimits(pageRel);
|
||
|
||
let range = this.Lines[PRS.Line].Ranges[PRS.Range];
|
||
X = X - range.X + pageLimits.XLimit - range.XEnd;
|
||
startX = pageLimits.XLimit - endX;
|
||
}
|
||
|
||
// Если у данного параграфа есть табы, тогда ищем среди них
|
||
// Добавим в качестве таба левую границу
|
||
let tabs = [];
|
||
let addLefInd = true;
|
||
let paraTabs = ParaPr.Tabs;
|
||
for (let tabIndex = 0, tabCount = paraTabs.GetCount(); tabIndex < tabCount; ++tabIndex)
|
||
{
|
||
let tab = paraTabs.Get(tabIndex);
|
||
let tabPos = tab.Pos + startX;
|
||
|
||
if (addLefInd && tabPos > startX + ParaPr.Ind.Left)
|
||
{
|
||
tabs.push(new CParaTab(tab_Left, ParaPr.Ind.Left));
|
||
addLefInd = false;
|
||
}
|
||
|
||
if (tab_Clear !== tab.Value)
|
||
tabs.push(tab);
|
||
}
|
||
|
||
if (addLefInd)
|
||
tabs.push(new CParaTab(tab_Left, ParaPr.Ind.Left));
|
||
|
||
let customTab = null;
|
||
for (let tabIndex = 0, tabCount = tabs.length; tabIndex < tabCount; ++tabIndex)
|
||
{
|
||
let tab = tabs[tabIndex];
|
||
|
||
// TODO: Пока здесь сделаем поправку на погрешность. Когда мы сделаем так, чтобы все наши значения хранились
|
||
// в тех же единицах, что и в формате Docx, тогда и здесь можно будет вернуть обычное сравнение (см. баг 22586)
|
||
// Разница с NumTab возникла из-за бага 22586, везде нестрогое оставлять нельзя из-за бага 32051.
|
||
|
||
let twX = AscCommon.MMToTwips(X);
|
||
let twTabPos = AscCommon.MMToTwips(tab.Pos + startX);
|
||
|
||
if ((true === NumTab && twX <= twTabPos) || (true !== NumTab && twX < twTabPos))
|
||
{
|
||
customTab = tab;
|
||
break;
|
||
}
|
||
}
|
||
|
||
var isTabToRightEdge = false;
|
||
|
||
var NewX = 0;
|
||
|
||
// Если табов нет, либо их позиции левее текущей позиции ставим таб по умолчанию
|
||
var DefTab = ParaPr.DefaultTab != null ? ParaPr.DefaultTab : AscCommonWord.Default_Tab_Stop;
|
||
if (customTab)
|
||
{
|
||
NewX = customTab.Pos + startX;
|
||
}
|
||
else
|
||
{
|
||
if ( X < startX + ParaPr.Ind.Left )
|
||
{
|
||
NewX = startX + ParaPr.Ind.Left;
|
||
}
|
||
else if (DefTab < 0.001)
|
||
{
|
||
NewX = X;
|
||
}
|
||
else
|
||
{
|
||
NewX = startX;
|
||
while ( X >= NewX - 0.001 )
|
||
NewX += DefTab;
|
||
}
|
||
|
||
// Так работает Word: если таб начался в допустимом отрезке, а заканчивается вне его,
|
||
// то мы ограничиваем его правым полем документа, но только если правый отступ параграфа
|
||
// неположителен (<= 0). (смотри bug 32345)
|
||
var twX = AscCommon.MMToTwips(X);
|
||
var twEndPos = AscCommon.MMToTwips(endX);
|
||
var twNewX = AscCommon.MMToTwips(NewX);
|
||
|
||
if (twX < twEndPos && twNewX >= twEndPos && AscCommon.MMToTwips(ParaPr.Ind.Right) <= 0)
|
||
{
|
||
NewX = endX;
|
||
isTabToRightEdge = true;
|
||
}
|
||
}
|
||
|
||
return {
|
||
TabWidth : NewX - X,
|
||
TabValue : customTab ? customTab.Value : tab_Left,
|
||
DefaultTab : !customTab,
|
||
TabLeader : customTab ? customTab.Leader : Asc.c_oAscTabLeader.None,
|
||
TabRightEdge : isTabToRightEdge,
|
||
PageX : startX,
|
||
PageXLimit : endX
|
||
};
|
||
};
|
||
|
||
Paragraph.prototype.private_CheckSkipKeepLinesAndWidowControl = function(CurPage)
|
||
{
|
||
var bSkipWidowAndKeepLines = false;
|
||
if (this.ColumnsCount > 1)
|
||
{
|
||
var bWrapDrawing = false;
|
||
for (var TempPage = 0; TempPage <= CurPage; ++TempPage)
|
||
{
|
||
for (var DrawingIndex = 0, DrawingsCount = this.Pages[TempPage].Drawings.length; DrawingIndex < DrawingsCount; ++DrawingIndex)
|
||
{
|
||
if (this.Pages[TempPage].Drawings[DrawingIndex].Use_TextWrap())
|
||
{
|
||
bWrapDrawing = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (bWrapDrawing)
|
||
break;
|
||
}
|
||
|
||
bSkipWidowAndKeepLines = bWrapDrawing;
|
||
}
|
||
|
||
return bSkipWidowAndKeepLines;
|
||
};
|
||
|
||
Paragraph.prototype.private_CheckColumnBreak = function(CurPage)
|
||
{
|
||
if (this.IsEmptyPage(CurPage))
|
||
return;
|
||
|
||
var Page = this.Pages[CurPage];
|
||
var Line = this.Lines[Page.EndLine];
|
||
|
||
if (!Line)
|
||
return;
|
||
|
||
if (Line.Info & paralineinfo_BreakPage && !(Line.Info & paralineinfo_BreakRealPage))
|
||
{
|
||
if (this.bFromDocument && this.LogicDocument)
|
||
this.LogicDocument.OnColumnBreak_WhileRecalculate();
|
||
}
|
||
};
|
||
|
||
Paragraph.prototype.private_RecalculateMoveLineToNextPage = function(CurLine, CurPage, PRS, ParaPr)
|
||
{
|
||
// TODO: Неразрывные абзацы и висячие строки внутри колонок вместе с плавающими объектами пока не обсчитываем
|
||
var bSkipWidowAndKeepLines = this.private_CheckSkipKeepLinesAndWidowControl(CurPage);
|
||
|
||
// Проверим висячую строку
|
||
if (this.Parent instanceof CDocument
|
||
&& false === bSkipWidowAndKeepLines
|
||
&& true === this.Parent.RecalcInfo.Can_RecalcWidowControl()
|
||
&& true === ParaPr.WidowControl
|
||
&& CurLine - this.Pages[CurPage].StartLine <= 1
|
||
&& CurLine >= 1 && true != PRS.BreakPageLine
|
||
&& ( 0 === CurPage && null != this.Get_DocumentPrev() ) )
|
||
{
|
||
// Вызываем данную функцию для удаления картинок с предыдущей страницы
|
||
this.Recalculate_Drawing_AddPageBreak(0, 0, true);
|
||
|
||
// TODO: Здесь перенос нужно делать сразу же, если в строке не было объектов с обтеканием
|
||
this.Parent.RecalcInfo.Set_WidowControl(this, CurLine - 1);
|
||
PRS.RecalcResult = recalcresult_CurPage | recalcresultflags_Column;
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
// Учитываем неразрывные абзацы:
|
||
// 1. В Word2010 (версия <= 14) просто проверяем, если параграф разбивается на 2 страницы, тогда
|
||
// переносим его с новой страницы. Также не учитываем неразрывные параграфы внутри таблиц.
|
||
// 2. В Word2016 (версия >= 15) в добавок к предыдущему ориентируемся на колонки: пытаемся текущую
|
||
// страницу параграфа разместить в какой либо колонке (пересчитывая их по очереди), если параграф
|
||
// все равно не рассчитан до конца, тогда размещаем его в первой колонке и делаем перенос на следующую
|
||
// страницу.
|
||
if (true === ParaPr.KeepLines && false === bSkipWidowAndKeepLines)
|
||
{
|
||
let compatibilityMode = PRS.getCompatibilityMode();
|
||
if (compatibilityMode <= AscCommon.document_compatibility_mode_Word14)
|
||
{
|
||
if (null != this.Get_DocumentPrev() && !this.IsTableCellContent() && 0 === CurPage)
|
||
{
|
||
CurLine = 0;
|
||
PRS.RunRecalcInfoBreak = null;
|
||
}
|
||
}
|
||
else if (compatibilityMode >= AscCommon.document_compatibility_mode_Word15)
|
||
{
|
||
// TODO: Разобраться с 2016 вордом
|
||
if (null != this.Get_DocumentPrev() && 0 === CurPage)
|
||
{
|
||
CurLine = 0;
|
||
PRS.RunRecalcInfoBreak = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Восстанавливаем позицию нижней границы предыдущей страницы
|
||
this.Pages[CurPage].Bounds.Bottom = PRS.LinePrevBottom;
|
||
this.Pages[CurPage].Set_EndLine( CurLine - 1 );
|
||
|
||
if ( 0 === CurLine )
|
||
this.Lines[-1] = new CParaLine(0);
|
||
|
||
// Добавляем разрыв страницы
|
||
PRS.RecalcResult = recalcresult_NextPage;
|
||
return false;
|
||
}
|
||
};
|
||
|
||
Paragraph.prototype.private_CheckNeedBeforeSpacing = function(CurPage, Parent, PageAbs, ParaPr)
|
||
{
|
||
if (CurPage <= 0)
|
||
{
|
||
let oPrevElement = this.GetPrevDocumentElement();
|
||
while (oPrevElement && !oPrevElement.IsInline())
|
||
{
|
||
oPrevElement = oPrevElement.GetPrevDocumentElement();
|
||
}
|
||
|
||
return (!oPrevElement
|
||
|| oPrevElement.GetAbsolutePage(oPrevElement.GetPagesCount() - 1) >= PageAbs
|
||
|| !oPrevElement.IsParagraph()
|
||
|| oPrevElement.Get_SectionPr());
|
||
}
|
||
|
||
if (!this.Check_FirstPage(CurPage))
|
||
{
|
||
// Если на предыдущих страницах были только разрывы страниц и колонок, тогда добавляем расстояние
|
||
if (this.Check_FirstPage(CurPage, true))
|
||
return true;
|
||
else
|
||
return false;
|
||
}
|
||
|
||
if (this.LogicDocument
|
||
&& this.LogicDocument.GetCompatibilityMode
|
||
&& this.LogicDocument.GetCompatibilityMode() <= AscCommon.document_compatibility_mode_Word14
|
||
&& true === ParaPr.PageBreakBefore)
|
||
return true;
|
||
|
||
let topDocument = this.GetTopDocumentContent();
|
||
if (!(topDocument instanceof AscWord.Document) || this.IsTableCellContent())
|
||
{
|
||
if (Parent instanceof AscFormat.CDrawingDocContent && 0 !== CurPage)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
// Если сюда дошли, значит мы либо на верхнем уровне, либо в блочном контроле, который лежит на верхнем уровне.
|
||
// Дальше все зависит от того на какой мы странице. Если на первой странице данной секции,
|
||
// тогда добавляем расстояние, а если нет - нет. Но подсчет первой страницы здесь не совпадает с тем, как она
|
||
// считается для нумерации. Если разрыв секции идет на текущей странице, то первой считается сразу данная страница.
|
||
|
||
let documentSections = topDocument.GetSections();
|
||
let sectionIndex = documentSections.GetIndexByElement(this);
|
||
let firstParagraph = -1 !== sectionIndex ? documentSections.GetFirstParagraph(sectionIndex) : null;
|
||
|
||
return (0 !== sectionIndex && (!firstParagraph || firstParagraph.GetAbsolutePage(0) === PageAbs));
|
||
};
|
||
|
||
Paragraph.prototype.ShapeText = function()
|
||
{
|
||
if (!this.RecalcInfo.ShapeText)
|
||
return;
|
||
|
||
// TODO: Код для теста скорости функции ShapeText
|
||
// let nRecalcId = this.LogicDocument ? this.LogicDocument.GetRecalcId() : -1;
|
||
// if (this.ShapeId === nRecalcId)
|
||
// return;
|
||
//
|
||
// this.ShapeId = nRecalcId;
|
||
|
||
// TODO: Сейчас мы шейпим текст целиком во всем параграфе. Для ускорения нужно отслеживать позиции, в которых
|
||
// произошли изменения (далее влево и вправо найти позиции пробела/таба или другого разделителя слова)
|
||
// и шейпить текст только в заданном промежутке
|
||
|
||
AscWord.ParagraphTextShaper.Shape(this);
|
||
this.RecalcInfo.ShapeText = false;
|
||
};
|
||
Paragraph.prototype.HyphenateText = function()
|
||
{
|
||
if (!this.RecalcInfo.HyphenateText || !this.isAutoHyphenation())
|
||
return;
|
||
|
||
AscWord.TextHyphenator.hyphenate(this);
|
||
};
|
||
|
||
Paragraph.prototype.ShapeTextInRange = function(oStartPos, oEndPos)
|
||
{
|
||
AscWord.ParagraphTextShaper.ShapeRange(this, oStartPos, oEndPos, true);
|
||
};
|
||
Paragraph.prototype.GetLigatureEndPos = function(oStartPos)
|
||
{
|
||
let oLigature = this.GetNextRunElement(oStartPos);
|
||
if (!oLigature || !oLigature.IsText())
|
||
return oStartPos;
|
||
|
||
if (!oLigature.IsLigature())
|
||
{
|
||
let oResultPos = oStartPos.Copy();
|
||
oResultPos.Update(oStartPos.GetPos(oStartPos.GetDepth()) + 1, oStartPos.GetDepth());
|
||
return oStartPos;
|
||
}
|
||
|
||
let oCurrentPos = oStartPos;
|
||
let oSearchPos = new CParagraphSearchPos();
|
||
this.Get_RightPos(oSearchPos, oCurrentPos, false);
|
||
|
||
while (oSearchPos.IsFound())
|
||
{
|
||
oCurrentPos = oSearchPos.GetPos().Copy();
|
||
|
||
let oNext = this.GetNextRunElement(oCurrentPos);
|
||
let oPrev = this.GetPrevRunElement(oCurrentPos);
|
||
|
||
if (!oPrev
|
||
|| !oNext
|
||
|| !oPrev.IsText()
|
||
|| !oNext.IsText()
|
||
|| !oNext.IsLigatureContinue())
|
||
break;
|
||
|
||
oSearchPos.Reset();
|
||
this.Get_RightPos(oSearchPos, oCurrentPos, false);
|
||
|
||
if (!oSearchPos.IsFound())
|
||
break;
|
||
}
|
||
|
||
return oCurrentPos;
|
||
};
|
||
Paragraph.prototype.CollectRunItemsInRange = function(oStartPos, oEndPos)
|
||
{
|
||
let arrPositions = [];
|
||
let arrItems = [];
|
||
|
||
this.CheckRunContent(function(oRun, nStartPos, nEndPos, oCurrentPos)
|
||
{
|
||
for (let nPos = nStartPos; nPos < nEndPos; ++nPos)
|
||
{
|
||
let oParaPos = oCurrentPos.Copy();
|
||
oParaPos.Add(nPos);
|
||
|
||
arrItems.push(oRun.GetElement(nPos));
|
||
arrPositions.push(oParaPos);
|
||
}
|
||
}, oStartPos, oEndPos, true);
|
||
|
||
return {
|
||
Positions : arrPositions,
|
||
Items : arrItems
|
||
};
|
||
};
|
||
Paragraph.prototype.FindLineBreakInLongWord = function(nWidth, oLineStartPos, oCurPos)
|
||
{
|
||
// TODO: Когда будут прокидываться типы HB_GLYPH_FLAG_UNSAFE_TO_BREAK, HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
|
||
// переделать здесь поиск начальной точки для формирования текста
|
||
|
||
let oInfo = this.CollectRunItemsInRange(oLineStartPos, oCurPos);
|
||
|
||
let arrPositions = oInfo.Positions;
|
||
let arrItems = oInfo.Items;
|
||
|
||
// По логике первая позиция ДОЛЖНА совпадать с oLineStartPos, поэтому
|
||
// мы не отдаем разрыв в первой позиции, чтобы как минимум 1 символ был на строке
|
||
if (arrPositions.length <= 1)
|
||
return oCurPos;
|
||
|
||
let oBreakPosition = oCurPos;
|
||
let nLastPos = arrPositions.length - 1;
|
||
while (nLastPos > 0)
|
||
{
|
||
// TODO: Возможно здесь проверку стоит изменить (или дополнить) на проверку может ли символ находится
|
||
// в начале строки и может ли предыдущий находится в конце строки
|
||
while (arrItems[nLastPos].IsCombiningMark() && nLastPos > 0)
|
||
{
|
||
nLastPos--;
|
||
}
|
||
|
||
if (0 === nLastPos)
|
||
return oBreakPosition;
|
||
|
||
oBreakPosition = arrPositions[nLastPos];
|
||
|
||
this.ShapeTextInRange(oLineStartPos, arrPositions[nLastPos]);
|
||
|
||
let nTempWidth = 0;
|
||
for (let nPos = 0; nPos < nLastPos; ++nPos)
|
||
{
|
||
nTempWidth += arrItems[nPos].GetWidth();
|
||
}
|
||
|
||
if (nTempWidth < nWidth)
|
||
return arrPositions[nLastPos];
|
||
|
||
nLastPos--;
|
||
}
|
||
|
||
this.ShapeTextInRange(oLineStartPos, arrPositions[1]);
|
||
return arrPositions[1];
|
||
};
|
||
Paragraph.prototype.Recalculate_SetRangeBounds = function(CurLine, CurRange, oStartPos, oEndPos)
|
||
{
|
||
let nStartPos = oStartPos.Get(0);
|
||
let nEndPos = oEndPos.Get(0);
|
||
|
||
for (let nPos = nStartPos; nPos <= nEndPos; ++nPos)
|
||
{
|
||
let oItem = this.Content[nPos];
|
||
if (nPos !== nStartPos)
|
||
oItem.Recalculate_Reset(CurRange, CurLine);
|
||
|
||
oItem.Recalculate_SetRangeBounds(CurLine, CurRange, nPos === nStartPos ? oStartPos : null, nPos === nEndPos ? oEndPos : null, 1);
|
||
}
|
||
};
|
||
Paragraph.prototype.GetContentWidthInRange = function(oStartPos, oEndPos)
|
||
{
|
||
let nWidth = 0;
|
||
|
||
let nStartPos = oStartPos && 0 <= oStartPos.GetDepth()? oStartPos.Get(0) : 0;
|
||
let nEndPos = oEndPos && 0 <= oEndPos.GetDepth() ? oEndPos.Get(0) : this.Content.length - 1;
|
||
|
||
for (let nPos = nStartPos; nPos <= nEndPos; ++nPos)
|
||
{
|
||
nWidth += this.Content[nPos].GetContentWidthInRange(nPos === nStartPos ? oStartPos : null, nPos === nEndPos ? oEndPos : null, 1);
|
||
}
|
||
|
||
return nWidth;
|
||
};
|
||
|
||
|
||
var ERecalcPageType =
|
||
{
|
||
START : 0x00, // начать заново пересчет, с начала страницы
|
||
ELEMENT : 0x01, // начать заново пересчет, начиная с заданного элемента
|
||
Y : 0x02 // начать заново пересчет, начиная с заданной позиции по вертикали
|
||
};
|
||
|
||
function CRecalcPageType()
|
||
{
|
||
this.Type = ERecalcPageType.START;
|
||
this.Element = null;
|
||
this.Y = -1;
|
||
}
|
||
|
||
CRecalcPageType.prototype.Reset = function()
|
||
{
|
||
this.Type = ERecalcPageType.START;
|
||
this.Element = null;
|
||
this.Y = -1;
|
||
};
|
||
CRecalcPageType.prototype.Set_Element = function(Element)
|
||
{
|
||
this.Type = ERecalcPageType.Element;
|
||
this.Element = Element;
|
||
};
|
||
CRecalcPageType.prototype.Set_Y = function(Y)
|
||
{
|
||
this.Type = ERecalcPageType.Y;
|
||
this.Y = Y;
|
||
};
|
||
|
||
var paralineinfo_BreakPage = 0x0001; // В строке есть PageBreak или ColumnBreak
|
||
var paralineinfo_Empty = 0x0002; // Строка пустая
|
||
var paralineinfo_End = 0x0004; // Последняя строка параграфа
|
||
var paralineinfo_RangeY = 0x0008; // Строка начинается после какого-либо объекта с обтеканием
|
||
var paralineinfo_BreakRealPage = 0x0010; // В строке есть PageBreak
|
||
var paralineinfo_BadLeftTab = 0x0020; // В строке есть левый таб, который правее правой границы
|
||
var paralineinfo_Notes = 0x0040; // В строке есть сноски
|
||
var paralineinfo_TextOnLine = 0x0080; // Есть ли в строке текст
|
||
var paralineinfo_BreakLine = 0x0100; // Строка закончилась переносом строки
|
||
var paralineinfo_LongWord = 0x0200; // В строке длинное слово, которое не убралось
|
||
let paralineinfo_AutoHyphen = 0x0400; // Строка закончилась автопереносом
|
||
|
||
function CParaLine()
|
||
{
|
||
this.Y = 0; // Позиция BaseLine
|
||
this.Top = 0;
|
||
this.Bottom = 0;
|
||
this.Metrics = new CParaLineMetrics();
|
||
this.Ranges = []; // Массив CParaLineRanges
|
||
this.Info = 0; // Побитовая информация о строке:
|
||
// 1 бит : есть ли PageBreak в строке
|
||
// 2 бит : пустая ли строка (без учета PageBreak)
|
||
// 3 бит : последняя ли это строка (т.е. строка с ParaEnd)
|
||
// 4 бит : строка переносится по Y по обтекаемому объекту
|
||
this.CF = [];
|
||
}
|
||
CParaLine.prototype =
|
||
{
|
||
Shift : function(Dx, Dy)
|
||
{
|
||
// По Y мы ничего не переносим, т.к. все значени по Y у строки относительно начала страницы данного параграфа
|
||
for (var CurRange = 0, RangesCount = this.Ranges.length; CurRange < RangesCount; CurRange++)
|
||
{
|
||
this.Ranges[CurRange].Shift(Dx, Dy);
|
||
}
|
||
},
|
||
|
||
Get_StartPos : function()
|
||
{
|
||
if (this.Ranges.length <= 0)
|
||
return 0;
|
||
|
||
return this.Ranges[0].StartPos;
|
||
},
|
||
|
||
Get_EndPos : function()
|
||
{
|
||
if (this.Ranges.length <= 0)
|
||
return 0;
|
||
|
||
return this.Ranges[this.Ranges.length - 1].EndPos;
|
||
},
|
||
|
||
Copy : function()
|
||
{
|
||
var NewLine = new CParaLine();
|
||
|
||
NewLine.Y = this.Y;
|
||
NewLine.Top = this.Top;
|
||
NewLine.Bottom = this.Bottom;
|
||
|
||
NewLine.Metrics.Ascent = this.Ascent;
|
||
NewLine.Metrics.Descent = this.Descent;
|
||
NewLine.Metrics.TextAscent = this.TextAscent;
|
||
NewLine.Metrics.TextAscent2 = this.TextAscent2;
|
||
NewLine.Metrics.TextDescent = this.TextDescent;
|
||
NewLine.Metrics.LineGap = this.LineGap;
|
||
|
||
for (var CurRange = 0, RangesCount = this.Ranges.length; CurRange < RangesCount; CurRange++)
|
||
{
|
||
NewLine.Ranges[CurRange] = this.Ranges[CurRange].Copy();
|
||
}
|
||
|
||
NewLine.Info = this.Info;
|
||
|
||
return NewLine;
|
||
},
|
||
|
||
Reset : function()
|
||
{
|
||
//this.Y = 0;
|
||
this.Top = 0;
|
||
this.Bottom = 0;
|
||
this.Metrics = new CParaLineMetrics();
|
||
this.Ranges = [];
|
||
this.Info = 0;
|
||
}
|
||
};
|
||
CParaLine.prototype.addRange = function(x, xEnd)
|
||
{
|
||
this.Ranges.push(new AscWord.CParaLineRange(x, xEnd));
|
||
};
|
||
CParaLine.prototype.setRangeStartPos = function(rangeIndex, startPos)
|
||
{
|
||
this.Ranges[rangeIndex].StartPos = startPos;
|
||
};
|
||
CParaLine.prototype.setRangeEndPos = function(rangeIndex, endPos)
|
||
{
|
||
this.Ranges[rangeIndex].EndPos = endPos;
|
||
};
|
||
|
||
function CParaLineMetrics()
|
||
{
|
||
this.Ascent = 0; // Высота над BaseLine
|
||
this.Descent = 0; // Высота после BaseLine
|
||
this.TextAscent = 0; // Высота текста над BaseLine
|
||
this.TextAscent2 = 0; // Высота текста над BaseLine
|
||
this.TextDescent = 0; // Высота текста после BaseLine
|
||
this.LineGap = 0; // Дополнительное расстояние между строками
|
||
}
|
||
|
||
CParaLineMetrics.prototype =
|
||
{
|
||
Update : function(TextAscent, TextAscent2, TextDescent, Ascent, Descent, ParaPr)
|
||
{
|
||
if ( TextAscent > this.TextAscent )
|
||
this.TextAscent = TextAscent;
|
||
|
||
if ( TextAscent2 > this.TextAscent2 )
|
||
this.TextAscent2 = TextAscent2;
|
||
|
||
if ( TextDescent > this.TextDescent )
|
||
this.TextDescent = TextDescent;
|
||
|
||
if ( Ascent > this.Ascent )
|
||
this.Ascent = Ascent;
|
||
|
||
if ( Descent > this.Descent )
|
||
this.Descent = Descent;
|
||
|
||
if ( this.Ascent < this.TextAscent )
|
||
this.Ascent = this.TextAscent;
|
||
|
||
if ( this.Descent < this.TextDescent )
|
||
this.Descent = this.TextDescent;
|
||
|
||
this.LineGap = this.Recalculate_LineGap( ParaPr, this.TextAscent, this.TextDescent );
|
||
|
||
if (Asc.linerule_AtLeast === ParaPr.Spacing.LineRule && (this.Ascent + this.Descent + this.LineGap) > (this.TextAscent + this.TextDescent))
|
||
{
|
||
// В такой ситуации Word располагает текст внизу строки
|
||
this.Ascent = this.Ascent + this.LineGap;
|
||
this.LineGap = 0;
|
||
}
|
||
},
|
||
|
||
Recalculate_LineGap : function(ParaPr, TextAscent, TextDescent)
|
||
{
|
||
var LineGap = 0;
|
||
switch ( ParaPr.Spacing.LineRule )
|
||
{
|
||
case Asc.linerule_Auto:
|
||
{
|
||
LineGap = ( TextAscent + TextDescent ) * ( ParaPr.Spacing.Line - 1 );
|
||
break;
|
||
}
|
||
case Asc.linerule_Exact:
|
||
{
|
||
var ExactValue = Math.max( 25.4 / 72, ParaPr.Spacing.Line );
|
||
LineGap = ExactValue - ( TextAscent + TextDescent );
|
||
|
||
var Gap = this.Ascent + this.Descent - ExactValue;
|
||
|
||
if ( Gap > 0 )
|
||
{
|
||
var DescentDiff = this.Descent - this.TextDescent;
|
||
|
||
if ( DescentDiff > 0 )
|
||
{
|
||
if ( DescentDiff < Gap )
|
||
{
|
||
this.Descent = this.TextDescent;
|
||
Gap -= DescentDiff;
|
||
}
|
||
else
|
||
{
|
||
this.Descent -= Gap;
|
||
Gap = 0;
|
||
}
|
||
}
|
||
|
||
var AscentDiff = this.Ascent - this.TextAscent;
|
||
|
||
if ( AscentDiff > 0 )
|
||
{
|
||
if ( AscentDiff < Gap )
|
||
{
|
||
this.Ascent = this.TextAscent;
|
||
Gap -= AscentDiff;
|
||
}
|
||
else
|
||
{
|
||
this.Ascent -= Gap;
|
||
Gap = 0;
|
||
}
|
||
}
|
||
|
||
if ( Gap > 0 )
|
||
{
|
||
// Уменьшаем пропорционально TextAscent и TextDescent
|
||
var OldTA = this.TextAscent;
|
||
var OldTD = this.TextDescent;
|
||
|
||
var Sum = OldTA + OldTD;
|
||
|
||
this.Ascent = OldTA * (Sum - Gap) / Sum;
|
||
this.Descent = OldTD * (Sum - Gap) / Sum;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
this.Ascent -= Gap; // все в Ascent
|
||
}
|
||
|
||
LineGap = 0;
|
||
|
||
|
||
break;
|
||
}
|
||
case Asc.linerule_AtLeast:
|
||
{
|
||
var TargetLineGap = ParaPr.Spacing.Line;
|
||
var TextLineGap = TextAscent + TextDescent;
|
||
var RealLineGap = this.Ascent + this.Descent;
|
||
|
||
// Специальный случай, когда в строке нет никакого текста
|
||
if (Math.abs(TextLineGap) < 0.001 || RealLineGap >= TargetLineGap)
|
||
LineGap = 0;
|
||
else
|
||
LineGap = TargetLineGap - RealLineGap;
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
return LineGap;
|
||
}
|
||
};
|
||
CParaLineMetrics.prototype.Copy = function()
|
||
{
|
||
var oMetrics = new CParaLineMetrics();
|
||
|
||
oMetrics.Ascent = this.Ascent;
|
||
oMetrics.Descent = this.Descent;
|
||
oMetrics.TextAscent = this.TextAscent;
|
||
oMetrics.TextAscent2 = this.TextAscent2;
|
||
oMetrics.TextDescent = this.TextDescent;
|
||
oMetrics.LineGap = this.LineGap;
|
||
|
||
return oMetrics;
|
||
};
|
||
CParaLineMetrics.prototype.IsEqual = function(oMetrics)
|
||
{
|
||
return (Math.abs(oMetrics.Ascent - this.Ascent) < 0.001
|
||
&& Math.abs(oMetrics.Descent - this.Descent) < 0.001
|
||
&& Math.abs(oMetrics.TextAscent - this.TextAscent) < 0.001
|
||
&& Math.abs(oMetrics.TextAscent2 - this.TextAscent2) < 0.001
|
||
&& Math.abs(oMetrics.TextDescent - this.TextDescent) < 0.001
|
||
&& Math.abs(oMetrics.LineGap - this.LineGap) < 0.001);
|
||
};
|
||
CParaLineMetrics.prototype.Reset = function()
|
||
{
|
||
this.Ascent = 0;
|
||
this.Descent = 0;
|
||
this.TextAscent = 0;
|
||
this.TextAscent2 = 0;
|
||
this.TextDescent = 0;
|
||
this.LineGap = 0;
|
||
};
|
||
|
||
function CParaLineRange(X, XEnd)
|
||
{
|
||
this.X = X; // Начальная позиция отрезка без учета прилегания содержимого
|
||
this.XVisible = 0; // Начальная позиция отрезка с учетом прилегания содержимого
|
||
this.XEnd = XEnd; // Предельное значение по X для данного отрезка
|
||
this.XEndVisible = X; // Где фактически заканчивается содержимое в данном отрезке
|
||
this.StartPos = 0; // Позиция в контенте параграфа, с которой начинается данный отрезок
|
||
this.EndPos = 0; // Позиция в контенте параграфа, на которой заканчиваетсяданный отрезок
|
||
this.W = 0;
|
||
this.Spaces = 0; // Количество пробелов в отрезке, без учета пробелов в конце отрезка
|
||
this.WEnd = 0; // Если есть знак конца параграфа в данном отрезке, то это его ширина
|
||
this.WBreak = 0; // Если в конце отрезка есть разрыв строки/колонки/страницы
|
||
}
|
||
|
||
CParaLineRange.prototype =
|
||
{
|
||
Shift : function(Dx, Dy)
|
||
{
|
||
this.X += Dx;
|
||
this.XEnd += Dx;
|
||
this.XVisible += Dx;
|
||
this.XEndVisible += Dx;
|
||
|
||
if (this.XEndOrigin)
|
||
this.XEndOrigin += Dx;
|
||
},
|
||
|
||
Copy : function()
|
||
{
|
||
var NewRange = new CParaLineRange();
|
||
|
||
NewRange.X = this.X;
|
||
NewRange.XVisible = this.XVisible;
|
||
NewRange.XEnd = this.XEnd;
|
||
NewRange.XEndVisible = this.XEndVisible;
|
||
NewRange.StartPos = this.StartPos;
|
||
NewRange.EndPos = this.EndPos;
|
||
NewRange.W = this.W;
|
||
NewRange.Spaces = this.Spaces;
|
||
|
||
if (this.XEndOrigin)
|
||
NewRange.XEndOrigin = this.XEndOrigin;
|
||
|
||
return NewRange;
|
||
}
|
||
};
|
||
CParaLineRange.prototype.CorrectX = function(nX)
|
||
{
|
||
let x = nX;
|
||
|
||
if (x > this.XEnd)
|
||
x = this.XEnd;
|
||
|
||
if (x < this.X)
|
||
x = this.X;
|
||
|
||
return x;
|
||
};
|
||
CParaLineRange.prototype.IsZeroRange = function()
|
||
{
|
||
return ((this.XEnd - this.X) < 0.001);
|
||
};
|
||
CParaLineRange.prototype.getXEndOrigin = function()
|
||
{
|
||
return (undefined !== this.XEndOrigin ? this.XEndOrigin : this.XEnd);
|
||
};
|
||
AscWord.CParaLineRange = CParaLineRange;
|
||
|
||
function CParaPage(X, Y, XLimit, YLimit, FirstLine)
|
||
{
|
||
this.X = X;
|
||
this.Y = Y;
|
||
this.XLimit = XLimit;
|
||
this.YLimit = YLimit;
|
||
this.FirstLine = FirstLine;
|
||
this.Bounds = new CDocumentBounds( X, Y, XLimit, Y );
|
||
this.StartLine = FirstLine; // Номер строки, с которой начинается данная страница
|
||
this.EndLine = FirstLine; // Номер последней строки на данной странице
|
||
this.TextPr = null; // Расситанные текстовые настройки для начала страницы
|
||
|
||
this.Drawings = [];
|
||
this.EndInfo = new CParagraphPageEndInfo();
|
||
}
|
||
|
||
CParaPage.prototype =
|
||
{
|
||
Reset : function(X, Y, XLimit, YLimit, FirstLine)
|
||
{
|
||
this.X = X;
|
||
this.Y = Y;
|
||
this.XLimit = XLimit;
|
||
this.YLimit = YLimit;
|
||
this.FirstLine = FirstLine;
|
||
this.Bounds = new CDocumentBounds( X, Y, XLimit, Y );
|
||
this.StartLine = FirstLine;
|
||
this.Drawings = [];
|
||
},
|
||
|
||
Shift : function(Dx, Dy)
|
||
{
|
||
this.X += Dx;
|
||
this.Y += Dy;
|
||
this.XLimit += Dx;
|
||
this.YLimit += Dy;
|
||
this.Bounds.Shift( Dx, Dy );
|
||
},
|
||
|
||
Set_EndLine : function(EndLine)
|
||
{
|
||
this.EndLine = EndLine;
|
||
},
|
||
|
||
Add_Drawing : function(Item)
|
||
{
|
||
this.Drawings.push(Item);
|
||
},
|
||
|
||
Copy : function()
|
||
{
|
||
var NewPage = new CParaPage();
|
||
|
||
NewPage.X = this.X;
|
||
NewPage.Y = this.Y;
|
||
NewPage.XLimit = this.XLimit;
|
||
NewPage.YLimit = this.YLimit;
|
||
NewPage.FirstLine = this.FirstLine;
|
||
|
||
NewPage.Bounds.Left = this.Bounds.Left;
|
||
NewPage.Bounds.Right = this.Bounds.Right;
|
||
NewPage.Bounds.Top = this.Bounds.Top;
|
||
NewPage.Bounds.Bottom = this.Bounds.Bottom;
|
||
|
||
NewPage.StartLine = this.StartLine;
|
||
NewPage.EndLine = this.EndLine;
|
||
|
||
var Count = this.Drawings.length;
|
||
for ( var Index = 0; Index < Count; Index++ )
|
||
{
|
||
NewPage.Drawings.push( this.Drawings[Index] );
|
||
}
|
||
|
||
NewPage.EndInfo = this.EndInfo.Copy();
|
||
|
||
return NewPage;
|
||
}
|
||
};
|
||
|
||
function CParagraphRecalculateTabInfo()
|
||
{
|
||
this.TabPos = 0;
|
||
this.X = 0;
|
||
this.Value = -1;
|
||
this.Item = null;
|
||
}
|
||
|
||
CParagraphRecalculateTabInfo.prototype =
|
||
{
|
||
Reset : function()
|
||
{
|
||
this.TabPos = 0;
|
||
this.X = 0;
|
||
this.Value = -1;
|
||
this.Item = null;
|
||
}
|
||
};
|
||
|
||
function ParagraphRecalculateStateBase()
|
||
{
|
||
this.locked = false;
|
||
}
|
||
ParagraphRecalculateStateBase.prototype.isLocked = function()
|
||
{
|
||
return this.locked;
|
||
};
|
||
ParagraphRecalculateStateBase.prototype.lock = function()
|
||
{
|
||
this.locked = true;
|
||
};
|
||
ParagraphRecalculateStateBase.prototype.unlock = function()
|
||
{
|
||
this.locked = false;
|
||
};
|
||
window['AscWord'].ParagraphRecalculateStateBase = ParagraphRecalculateStateBase;
|
||
|
||
function ParagraphStatePool()
|
||
{
|
||
this.wrap = [];
|
||
this.endInfo = [];
|
||
this.draw = [];
|
||
}
|
||
ParagraphStatePool.prototype.getInstance = function(pool, className)
|
||
{
|
||
let instance = null;
|
||
for (let i = 0, n = pool.length; i < n; ++i)
|
||
{
|
||
if (!pool[i].isLocked())
|
||
{
|
||
instance = pool[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!instance)
|
||
{
|
||
instance = new className();
|
||
pool.push(instance);
|
||
}
|
||
|
||
instance.lock();
|
||
return instance;
|
||
};
|
||
ParagraphStatePool.prototype.release = function(instance)
|
||
{
|
||
instance.unlock();
|
||
};
|
||
ParagraphStatePool.prototype.getWrapState = function()
|
||
{
|
||
return this.getInstance(this.wrap, CParagraphRecalculateStateWrap);
|
||
};
|
||
ParagraphStatePool.prototype.getEndInfoState = function()
|
||
{
|
||
return this.getInstance(this.endInfo, CParagraphRecalculateStateInfo);
|
||
};
|
||
ParagraphStatePool.prototype.getDrawState = function()
|
||
{
|
||
return this.getInstance(this.draw, AscWord.ParagraphDrawState);
|
||
};
|
||
window['AscWord'].ParagraphStatePool = new ParagraphStatePool();
|
||
|
||
function CParagraphRecalculateStateWrap()
|
||
{
|
||
ParagraphRecalculateStateBase.call(this);
|
||
|
||
// Общие параметры, которые заполняются 1 раз на пересчет всей страницы
|
||
this.Paragraph = null;
|
||
this.Parent = null;
|
||
this.TopDocument = null;
|
||
this.TopIndex = -1; // Номер элемента контейнера (содержащего данный параграф), либо номер данного параграфа в самом верхнем документе
|
||
this.PageAbs = 0;
|
||
this.ColumnAbs = 0;
|
||
this.InTable = false;
|
||
this.SectPr = null; // настройки секции, к которой относится данный параграф
|
||
this.CondensedSpaces = false;
|
||
this.BalanceSBDB = false; // BalanceSingleByteDoubleByteWidth
|
||
this.autoHyphenation = false;
|
||
|
||
this.Fast = false; // Быстрый ли пересчет
|
||
|
||
this.alignState = new CParagraphRecalculateStateAlign(this);
|
||
this.counterState = new CParagraphRecalculateStateCounter(this);
|
||
|
||
//
|
||
this.Page = 0;
|
||
this.Line = 0;
|
||
this.Range = 0;
|
||
|
||
this.Ranges = [];
|
||
this.RangesCount = 0;
|
||
|
||
this.LineY = [];
|
||
|
||
this.FirstItemOnLine = true;
|
||
this.PrevItemFirst = false;
|
||
this.EmptyLine = true;
|
||
this.StartWord = false;
|
||
this.Word = false;
|
||
this.AddNumbering = true;
|
||
this.TextOnLine = false;
|
||
this.RangeSpaces = [];
|
||
|
||
this.BreakPageLine = false; // Разрыв страницы (параграфа) в данной строке
|
||
this.UseFirstLine = false;
|
||
this.BreakPageLineEmpty = false;
|
||
this.BreakRealPageLine = false; // Разрыв страницы документа (не только параграфа) в данной строке
|
||
this.BadLeftTab = false; // Левый таб правее правой границы
|
||
this.BreakLine = false; // Строка закончилась принудительным разрывом
|
||
this.LongWord = false;
|
||
|
||
this.ComplexFields = new AscWord.ParagraphComplexFieldStack();
|
||
|
||
this.WordLen = 0;
|
||
this.SpaceLen = 0;
|
||
this.SpacesCount = 0;
|
||
this.LastTab = new CParagraphRecalculateTabInfo();
|
||
|
||
this.LineTextAscent = 0;
|
||
this.LineTextDescent = 0;
|
||
this.LineTextAscent2 = 0;
|
||
this.LineAscent = 0;
|
||
this.LineDescent = 0;
|
||
|
||
this.LineTop = 0;
|
||
this.LineBottom = 0;
|
||
this.LineTop2 = 0;
|
||
this.LineBottom2 = 0;
|
||
this.LinePrevBottom = 0;
|
||
|
||
this.XRange = 0; // Начальное положение по горизонтали для данного отрезка
|
||
this.X = 0; // Текущее положение по горизонтали
|
||
this.XEnd = 0; // Предельное значение по горизонтали для текущего отрезка
|
||
|
||
this.Y = 0; // Текущее положение по вертикали
|
||
|
||
this.XStart = 0; // Начальное значение для X на данной страницы
|
||
this.YStart = 0; // Начальное значение для Y на данной страницы
|
||
this.XLimit = 0; // Предельное значение для X на данной страницы
|
||
this.YLimit = 0; // Предельное значение для Y на данной страницы
|
||
|
||
this.NewPage = false; // Переходим на новую страницу
|
||
this.NewRange = false; // Переходим к новому отрезку
|
||
this.End = false;
|
||
this.RangeY = false; // Текущая строка переносится по Y из-за обтекания
|
||
|
||
this.CurPos = new AscWord.CParagraphContentPos();
|
||
|
||
this.NumberingPos = new AscWord.CParagraphContentPos(); // Позиция элемента вместе с которым идет нумерация
|
||
|
||
this.MoveToLBP = false; // Делаем ли разрыв в позиции this.LineBreakPos
|
||
this.UpdateLBP = true; // Флаг для первичного обновления позиции переноса в отрезке
|
||
this.LineBreakFirst = true; // Последняя позиция для переноса - это первый элемент в отрезке
|
||
|
||
// Последняя позиция в которой можно будет добавить разрыв отрезка или строки, если что-то не умещается (например,
|
||
// если у нас не убирается слово, то разрыв ставим перед ним)
|
||
this.LineBreakPos = new AscWord.CParagraphContentPos();
|
||
|
||
this.LastItem = null; // Последний непробельный элемент
|
||
this.LastItemRun = null; // Run, в котором лежит последний элемент LastItem
|
||
this.LastHyphenItem = null;
|
||
this.lastAutoHyphen = null; // Последний элемент с переносом, который убирался в отрезке вместо с дефисом
|
||
this.autoHyphenLimit = 0;
|
||
this.hyphenationZone = 0;
|
||
|
||
this.RunRecalcInfoLast = null; // RecalcInfo последнего рана
|
||
this.RunRecalcInfoBreak = null; // RecalcInfo рана, на котором произошел разрыв отрезка/строки
|
||
|
||
this.BaseLineOffset = 0;
|
||
|
||
this.RecalcResult = 0x00;//recalcresult_NextElement;
|
||
|
||
// Управляющий объект для пересчета неинлайновой формулы
|
||
this.MathRecalcInfo = {
|
||
Line : 0, // Номер строки, с которой начинается формула на текущей странице
|
||
Math : null // Сам объект формулы
|
||
};
|
||
|
||
this.Footnotes = [];
|
||
this.FootnotesRecalculateObject = null;
|
||
|
||
this.Endnotes = [];
|
||
|
||
// for ParaMath
|
||
this.bMath_OneLine = false;
|
||
this.bMathWordLarge = false;
|
||
this.bEndRunToContent = false;
|
||
this.PosEndRun = new AscWord.CParagraphContentPos();
|
||
|
||
// параметры, необходимые для расчета разбиения по операторам
|
||
// у "крайних" в строке операторов/мат объектов сооответствующий Gap равен нулю
|
||
this.OperGapRight = 0;
|
||
this.OperGapLeft = 0;
|
||
this.bPriorityOper = true; // есть ли в контенте операторы с высоким приоритетом разбиения
|
||
this.WrapIndent = 0; // WrapIndent нужен для сравнения с длиной слова (когда слово разбивается по Compare Oper): ширина первой строки формулы не должна быть меньше WrapIndent
|
||
this.bContainCompareOper = true; // содержаться ли в текущем контенте операторы с высоким приоритетом
|
||
this.MathFirstItem = true; // параметр необходим для принудительного переноса
|
||
this.bFirstLine = false;
|
||
|
||
this.bNoOneBreakOperator = true; // прежде чем обновлять позицию в контент Run, учтем были ли до этого break-операторы (проверки на Word == false не достаточно, т.к. формула мб инлайновая и тогда не нужно обновлять позицию)
|
||
this.bForcedBreak = false;
|
||
this.bInsideOper = false; // учитываем есть ли разбивка внутри мат объекта, чтобы случайно не вставить в конец пред оператора (при Brk_Before == false)
|
||
this.bOnlyForcedBreak = false; // учитывается, если возможна разбивка только по операторам выше уровням => в этом случае можно сделать принудительный разрыв во внутреннем контенте
|
||
this.bBreakBox = false;
|
||
|
||
//-----------------------------//
|
||
this.bFastRecalculate = false;
|
||
this.bBreakPosInLWord = true; // обновляем LineBreakPos (Set_LineBreakPos) для WordLarge. Не обновляем для инлайновой формулы, перед формулой есть еще текст, чтобы не перебить LineBreakPos и выставить по тем меткам, которые были до формулы разбиение
|
||
this.bContinueRecalc = false;
|
||
this.bMathRangeY = false; // используется для переноса формулы под картинку
|
||
this.MathNotInline = null;
|
||
}
|
||
CParagraphRecalculateStateWrap.prototype = Object.create(ParagraphRecalculateStateBase.prototype);
|
||
CParagraphRecalculateStateWrap.prototype.constructor = CParagraphRecalculateStateWrap;
|
||
|
||
CParagraphRecalculateStateWrap.prototype.getAlignState = function()
|
||
{
|
||
return this.alignState;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getCounterState = function()
|
||
{
|
||
return this.counterState;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Reset_Page = function(Paragraph, CurPage)
|
||
{
|
||
this.Paragraph = Paragraph;
|
||
this.Parent = Paragraph.Parent;
|
||
this.TopDocument = Paragraph.Parent.GetTopDocumentContent();
|
||
this.PageAbs = Paragraph.GetAbsolutePage(CurPage);
|
||
this.ColumnAbs = Paragraph.GetAbsoluteColumn(CurPage);
|
||
this.InTable = Paragraph.IsTableCellContent();
|
||
this.SectPr = null;
|
||
this.TopIndex = -1;
|
||
|
||
this.CondensedSpaces = Paragraph.IsCondensedSpaces();
|
||
this.BalanceSBDB = Paragraph.IsBalanceSingleByteDoubleByteWidth();
|
||
|
||
let settings = this.getDocumentSettings();
|
||
this.autoHyphenation = settings.isAutoHyphenation();
|
||
this.autoHyphenLimit = settings.getConsecutiveHyphenLimit();
|
||
this.hyphenationZone = AscCommon.TwipsToMM(settings.getHyphenationZone());
|
||
|
||
if (settings.getCompatibilityMode() >= AscCommon.document_compatibility_mode_Word15)
|
||
this.hyphenationZone = AscCommon.TwipsToMM(AscWord.DEFAULT_HYPHENATION_ZONE);
|
||
|
||
this.Page = CurPage;
|
||
this.RunRecalcInfoLast = (0 === CurPage ? null : Paragraph.Pages[CurPage - 1].EndInfo.RunRecalcInfo);
|
||
this.RunRecalcInfoBreak = this.RunRecalcInfoLast;
|
||
|
||
this.ComplexFields.resetPage(Paragraph, CurPage);
|
||
this.alignState.ComplexFields.resetPage(Paragraph, CurPage);
|
||
this.counterState.ComplexFields.resetPage(Paragraph, CurPage);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Reset_Line = function()
|
||
{
|
||
this.RecalcResult = recalcresult_NextLine;
|
||
|
||
this.EmptyLine = true;
|
||
this.BreakPageLine = false;
|
||
this.BreakLine = false;
|
||
this.LongWord = false;
|
||
this.End = false;
|
||
this.UseFirstLine = false;
|
||
this.BreakRealPageLine = false;
|
||
this.BadLeftTab = false
|
||
this.TextOnLine = false;
|
||
|
||
this.LineTextAscent = 0;
|
||
this.LineTextAscent2 = 0;
|
||
this.LineTextDescent = 0;
|
||
this.LineAscent = 0;
|
||
this.LineDescent = 0;
|
||
|
||
this.NewPage = false;
|
||
this.ForceNewPage = false;
|
||
this.ForceNewLine = false;
|
||
this.ForceNewPageAfter = false;
|
||
|
||
this.bMath_OneLine = false;
|
||
this.bMathWordLarge = false;
|
||
this.bEndRunToContent = false;
|
||
this.PosEndRun = new AscWord.CParagraphContentPos();
|
||
this.Footnotes = [];
|
||
this.Endnotes = [];
|
||
|
||
this.OperGapRight = 0;
|
||
this.OperGapLeft = 0;
|
||
this.WrapIndent = 0;
|
||
this.MathFirstItem = true;
|
||
this.bContainCompareOper = true;
|
||
this.bInsideOper = false;
|
||
this.bOnlyForcedBreak = false;
|
||
this.bBreakBox = false;
|
||
this.bNoOneBreakOperator = true;
|
||
this.bFastRecalculate = false;
|
||
this.bForcedBreak = false;
|
||
this.bBreakPosInLWord = true;
|
||
|
||
this.MathNotInline = null;
|
||
|
||
this.LineY.length = this.Line + 1;
|
||
if (this.Line >= 0)
|
||
this.LineY[this.Line] = this.Y;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.resetRange = function(range)
|
||
{
|
||
this.LastTab.Reset();
|
||
|
||
this.BreakLine = false;
|
||
this.SpaceLen = 0;
|
||
this.WordLen = 0;
|
||
this.SpacesCount = 0;
|
||
this.Word = false;
|
||
this.FirstItemOnLine = true;
|
||
this.StartWord = false;
|
||
this.NewRange = false;
|
||
this.X = range.X;
|
||
this.XEnd = range.XEnd;
|
||
this.XRange = range.X;
|
||
this.RangeSpaces = [];
|
||
|
||
this.MoveToLBP = false;
|
||
this.LineBreakPos = new AscWord.CParagraphContentPos();
|
||
this.LineBreakFirst = true;
|
||
this.LastItem = null;
|
||
this.LastItemRun = null;
|
||
this.UpdateLBP = true;
|
||
this.LastHyphenItem = null;
|
||
this.lastAutoHyphen = null;
|
||
|
||
// for ParaMath
|
||
this.bMath_OneLine = false;
|
||
this.bMathWordLarge = false;
|
||
this.bEndRunToContent = false;
|
||
this.PosEndRun = new AscWord.CParagraphContentPos();
|
||
|
||
this.OperGapRight = 0;
|
||
this.OperGapLeft = 0;
|
||
this.WrapIndent = 0;
|
||
this.bContainCompareOper = true;
|
||
this.bInsideOper = false;
|
||
this.bOnlyForcedBreak = false;
|
||
this.bBreakBox = false;
|
||
this.bNoOneBreakOperator = true;
|
||
this.bForcedBreak = false;
|
||
this.bFastRecalculate = false;
|
||
this.bBreakPosInLWord = true;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Set_LineBreakPos = function(PosObj, isFirstItemOnLine)
|
||
{
|
||
this.LineBreakPos.Set(this.CurPos);
|
||
this.LineBreakPos.Add(PosObj);
|
||
this.LineBreakFirst = isFirstItemOnLine;
|
||
this.ResetLastAutoHyphen();
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getDocumentSettings = function()
|
||
{
|
||
let logicDocument = this.Paragraph.GetLogicDocument();
|
||
if (logicDocument && logicDocument.IsDocumentEditor())
|
||
return logicDocument.getDocumentSettings();
|
||
|
||
return AscWord.DEFAULT_DOCUMENT_SETTINGS;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.isDocumentEditor = function()
|
||
{
|
||
let logicDocument = this.Paragraph.GetLogicDocument();
|
||
return (logicDocument && logicDocument.IsDocumentEditor());
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getCompatibilityMode = function()
|
||
{
|
||
return this.getDocumentSettings().getCompatibilityMode();
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getXLimit = function()
|
||
{
|
||
// TODO: Когда перенесем весь расчет в данный класс (из Run.Recalculate_Range), то
|
||
// при изменении XEnd сразу расчитывать это значение и заменить вызов на простой this.XEnd
|
||
return this.Paragraph.IsUseXLimit() ? this.XEnd : MEASUREMENT_MAX_MM_VALUE * 10;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.ResetLastAutoHyphen = function()
|
||
{
|
||
if (!this.LastHyphenItem)
|
||
return;
|
||
|
||
this.LastHyphenItem.SetTemporaryHyphenAfter(false);
|
||
this.LastHyphenItem = null;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.checkLastAutoHyphen = function()
|
||
{
|
||
if (!this.isAutoHyphenation())
|
||
return;
|
||
|
||
this.ResetLastAutoHyphen();
|
||
let lastItem = this.LastItem;
|
||
if (!lastItem || lastItem !== this.lastAutoHyphen)
|
||
return;
|
||
|
||
if (this.isExceedConsecutiveAutoHyphenLimit())
|
||
return;
|
||
|
||
this.LastHyphenItem = lastItem;
|
||
lastItem.SetTemporaryHyphenAfter(true);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Set_NumberingPos = function(PosObj, Item)
|
||
{
|
||
this.NumberingPos.Set(this.CurPos);
|
||
this.NumberingPos.Add(PosObj);
|
||
|
||
this.Paragraph.Numbering.Pos = this.NumberingPos;
|
||
this.Paragraph.Numbering.Item = Item;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Update_CurPos = function(PosObj, Depth)
|
||
{
|
||
this.CurPos.Update(PosObj, Depth);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Reset_Ranges = function()
|
||
{
|
||
this.Ranges = [];
|
||
this.RangesCount = 0;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Reset_RunRecalcInfo = function()
|
||
{
|
||
this.RunRecalcInfoBreak = this.RunRecalcInfoLast;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Reset_MathRecalcInfo = function()
|
||
{
|
||
this.bContinueRecalc = false;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Restore_RunRecalcInfo = function()
|
||
{
|
||
this.RunRecalcInfoLast = this.RunRecalcInfoBreak;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.Recalculate_Numbering = function(Item, Run, ParaPr, _X)
|
||
{
|
||
var CurPage = this.Page, CurLine = this.Line, CurRange = this.Range;
|
||
var Para = this.Paragraph;
|
||
var X = _X, LineAscent = this.LineAscent;
|
||
|
||
// Если нужно добавить нумерацию и на текущем элементе ее можно добавить, тогда добавляем её
|
||
var NumberingItem = Para.Numbering;
|
||
var NumberingType = Para.Numbering.Type;
|
||
|
||
if (para_Numbering === NumberingType)
|
||
{
|
||
var oReviewInfo = this.Paragraph.GetReviewInfo();
|
||
var nReviewType = this.Paragraph.GetReviewType();
|
||
|
||
var isHavePrChange = this.Paragraph.HavePrChange();
|
||
var oPrevNumPr = this.Paragraph.GetPrChangeNumPr();
|
||
|
||
var NumPr = ParaPr.NumPr;
|
||
|
||
if (!NumPr || !NumPr.IsValid())
|
||
NumPr = undefined;
|
||
|
||
if (!oPrevNumPr || !oPrevNumPr.IsValid())
|
||
{
|
||
oPrevNumPr = undefined;
|
||
}
|
||
else
|
||
{
|
||
oPrevNumPr = oPrevNumPr.Copy();
|
||
if (undefined === oPrevNumPr.Lvl)
|
||
oPrevNumPr.Lvl = 0;
|
||
}
|
||
|
||
var isHaveNumbering = false;
|
||
if ((undefined === Para.Get_SectionPr() || true !== Para.IsEmpty()) && (NumPr || oPrevNumPr))
|
||
{
|
||
isHaveNumbering = true;
|
||
}
|
||
|
||
if (!isHaveNumbering || (!NumPr && !oPrevNumPr) || (!NumPr && reviewtype_Add === nReviewType))
|
||
{
|
||
// Так мы обнуляем все рассчитанные ширины данного элемента
|
||
NumberingItem.Measure(g_oTextMeasurer, undefined);
|
||
}
|
||
else
|
||
{
|
||
var oSavedNumberingValues = this.Paragraph.GetSavedNumberingValues();
|
||
var arrSavedNumInfo = oSavedNumberingValues ? oSavedNumberingValues.NumInfo : null;
|
||
var arrSavedPrevNumInfo = oSavedNumberingValues ? oSavedNumberingValues.PrevNumInfo : null;
|
||
|
||
var oNumbering = Para.Parent.GetNumbering();
|
||
|
||
var oNumLvl = null;
|
||
let nNumSuff = Asc.c_oAscNumberingSuff.None;
|
||
|
||
if (NumPr)
|
||
{
|
||
oNumLvl = oNumbering.GetNum(NumPr.NumId).GetLvl(NumPr.Lvl);
|
||
nNumSuff = oNumLvl.GetSuff();
|
||
}
|
||
else if (oPrevNumPr)
|
||
{
|
||
// MSWord uses tab instead of suff from PrevNum (74525)
|
||
oNumLvl = oNumbering.GetNum(oPrevNumPr.NumId).GetLvl(oPrevNumPr.Lvl);
|
||
nNumSuff = Asc.c_oAscNumberingSuff.Tab;
|
||
}
|
||
|
||
var oNumTextPr = Para.GetNumberingTextPr();
|
||
var nNumJc = oNumLvl.GetJc();
|
||
|
||
// Здесь измеряется только ширина символов нумерации, без суффикса
|
||
if ((!isHavePrChange && NumPr) || (oPrevNumPr && NumPr && oPrevNumPr.NumId === NumPr.NumId && oPrevNumPr.Lvl === NumPr.Lvl))
|
||
{
|
||
var arrNumInfo = arrSavedNumInfo ? arrSavedNumInfo : Para.Parent.CalculateNumberingValues(Para, NumPr, true);
|
||
var nLvl = NumPr.Lvl;
|
||
|
||
var arrRelatedLvls = oNumLvl.GetRelatedLvlList();
|
||
var isEqual = true;
|
||
for (var nLvlIndex = 0, nLvlsCount = arrRelatedLvls.length; nLvlIndex < nLvlsCount; ++nLvlIndex)
|
||
{
|
||
var nTempLvl = arrRelatedLvls[nLvlIndex];
|
||
if (arrNumInfo[0][nTempLvl] !== arrNumInfo[1][nTempLvl])
|
||
{
|
||
isEqual = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!isEqual)
|
||
{
|
||
if (reviewtype_Common === nReviewType)
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr, arrNumInfo[1], NumPr);
|
||
}
|
||
else
|
||
{
|
||
if (reviewtype_Remove === nReviewType && oReviewInfo.GetPrevAdded())
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, undefined, undefined);
|
||
}
|
||
else if (reviewtype_Remove === nReviewType)
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, arrNumInfo[1], NumPr);
|
||
}
|
||
else
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr, undefined, undefined);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (reviewtype_Remove === nReviewType)
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, arrNumInfo[1], NumPr);
|
||
else
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr);
|
||
}
|
||
}
|
||
else if (oPrevNumPr && !NumPr)
|
||
{
|
||
var arrNumInfo2 = arrSavedPrevNumInfo ? arrSavedPrevNumInfo : Para.Parent.CalculateNumberingValues(Para, oPrevNumPr, true);
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, arrNumInfo2[1], oPrevNumPr);
|
||
}
|
||
else if (isHavePrChange && !oPrevNumPr && NumPr)
|
||
{
|
||
if (reviewtype_Remove === nReviewType)
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, undefined, undefined);
|
||
}
|
||
else
|
||
{
|
||
var arrNumInfo = arrSavedNumInfo ? arrSavedNumInfo : Para.Parent.CalculateNumberingValues(Para, NumPr, true);
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr, undefined, undefined);
|
||
}
|
||
}
|
||
else if (oPrevNumPr && NumPr)
|
||
{
|
||
var arrNumInfo = arrSavedNumInfo ? arrSavedNumInfo : Para.Parent.CalculateNumberingValues(Para, NumPr, true);
|
||
var arrNumInfo2 = arrSavedPrevNumInfo ? arrSavedPrevNumInfo : Para.Parent.CalculateNumberingValues(Para, oPrevNumPr, true);
|
||
|
||
var isEqual = false;
|
||
if (arrNumInfo[0][NumPr.Lvl] === arrNumInfo[1][oPrevNumPr.Lvl])
|
||
{
|
||
var oSourceNumLvl = oNumbering.GetNum(oPrevNumPr.NumId).GetLvl(oPrevNumPr.Lvl);
|
||
var oFinalNumLvl = oNumbering.GetNum(NumPr.NumId).GetLvl(NumPr.Lvl);
|
||
|
||
isEqual = oSourceNumLvl.IsSimilar(oFinalNumLvl);
|
||
if (isEqual)
|
||
{
|
||
var arrRelatedLvls = oSourceNumLvl.GetRelatedLvlList();
|
||
for (var nLvlIndex = 0, nLvlsCount = arrRelatedLvls.length; nLvlIndex < nLvlsCount; ++nLvlIndex)
|
||
{
|
||
var nTempLvl = arrRelatedLvls[nLvlIndex];
|
||
if (arrNumInfo[0][nTempLvl] !== arrNumInfo[1][nTempLvl])
|
||
{
|
||
isEqual = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isEqual)
|
||
{
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr);
|
||
}
|
||
else
|
||
{
|
||
if (reviewtype_Remove === nReviewType)
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), undefined, undefined, arrNumInfo2[1], oPrevNumPr);
|
||
else if (reviewtype_Add === nReviewType)
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr, undefined, undefined);
|
||
else
|
||
NumberingItem.Measure(g_oTextMeasurer, oNumbering, oNumTextPr, Para.Get_Theme(), arrNumInfo[0], NumPr, arrNumInfo2[1], oPrevNumPr);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Такого быть не должно
|
||
}
|
||
|
||
// При рассчете высоты строки, если у нас параграф со списком, то размер символа
|
||
// в списке влияет только на высоту строки над Baseline, но не влияет на высоту строки
|
||
// ниже baseline.
|
||
if (LineAscent < NumberingItem.Height)
|
||
LineAscent = NumberingItem.Height;
|
||
|
||
switch (nNumJc)
|
||
{
|
||
case AscCommon.align_Right:
|
||
{
|
||
NumberingItem.WidthVisible = 0;
|
||
break;
|
||
}
|
||
case AscCommon.align_Center:
|
||
{
|
||
NumberingItem.WidthVisible = NumberingItem.WidthNum / 2;
|
||
break;
|
||
}
|
||
case AscCommon.align_Left:
|
||
default:
|
||
{
|
||
NumberingItem.WidthVisible = NumberingItem.WidthNum;
|
||
break;
|
||
}
|
||
}
|
||
|
||
X += NumberingItem.WidthVisible;
|
||
|
||
if (oNumLvl.IsLegacy())
|
||
{
|
||
var nLegacySpace = AscCommon.TwipsToMM(oNumLvl.GetLegacySpace());
|
||
var nLegacyIndent = AscCommon.TwipsToMM(oNumLvl.GetLegacyIndent());
|
||
var nNumWidth = NumberingItem.WidthNum;
|
||
|
||
NumberingItem.WidthSuff = Math.max(nNumWidth, nLegacyIndent, nNumWidth + nLegacySpace) - nNumWidth;
|
||
}
|
||
else
|
||
{
|
||
switch (nNumSuff)
|
||
{
|
||
case Asc.c_oAscNumberingSuff.None:
|
||
{
|
||
// Ничего не делаем
|
||
break;
|
||
}
|
||
case Asc.c_oAscNumberingSuff.Space:
|
||
{
|
||
var OldTextPr = g_oTextMeasurer.GetTextPr();
|
||
|
||
|
||
var Theme = Para.Get_Theme();
|
||
g_oTextMeasurer.SetTextPr(oNumTextPr, Theme);
|
||
g_oTextMeasurer.SetFontSlot(AscWord.fontslot_ASCII);
|
||
NumberingItem.WidthSuff = g_oTextMeasurer.Measure(" ").Width;
|
||
g_oTextMeasurer.SetTextPr(OldTextPr, Theme);
|
||
break;
|
||
}
|
||
case Asc.c_oAscNumberingSuff.Tab:
|
||
{
|
||
NumberingItem.WidthSuff = Para.private_RecalculateGetTabPos(this, X, ParaPr, CurPage, true).TabWidth;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
NumberingItem.Width = NumberingItem.WidthNum;
|
||
NumberingItem.WidthVisible += NumberingItem.WidthSuff;
|
||
|
||
X += NumberingItem.WidthSuff;
|
||
}
|
||
}
|
||
else if (para_PresentationNumbering === NumberingType)
|
||
{
|
||
var Level = Para.PresentationPr.Level;
|
||
var Bullet = Para.PresentationPr.Bullet;
|
||
|
||
var BulletNum = Para.GetBulletNum();
|
||
if (BulletNum === null)
|
||
{
|
||
BulletNum = 1;
|
||
}
|
||
// Найдем настройки для первого текстового элемента
|
||
var FirstTextPr = Para.Get_FirstTextPr2();
|
||
|
||
|
||
if (Bullet.IsAlpha())
|
||
{
|
||
if (BulletNum > 780)
|
||
{
|
||
BulletNum = (BulletNum % 780);
|
||
}
|
||
}
|
||
if (BulletNum > 32767)
|
||
{
|
||
BulletNum = (BulletNum % 32767);
|
||
}
|
||
|
||
|
||
NumberingItem.Bullet = Bullet;
|
||
NumberingItem.BulletNum = BulletNum;
|
||
NumberingItem.Measure(g_oTextMeasurer, FirstTextPr, Para.Get_Theme(), Para.Get_ColorMap());
|
||
|
||
|
||
if (!Bullet.IsNone())
|
||
{
|
||
if (ParaPr.Ind.FirstLine < 0)
|
||
NumberingItem.WidthVisible = Math.max(NumberingItem.Width, Para.Pages[CurPage].X + ParaPr.Ind.Left + ParaPr.Ind.FirstLine - X, Para.Pages[CurPage].X + ParaPr.Ind.Left - X);
|
||
else
|
||
NumberingItem.WidthVisible = Math.max(Para.Pages[CurPage].X + ParaPr.Ind.Left + NumberingItem.Width - X, Para.Pages[CurPage].X + ParaPr.Ind.Left + ParaPr.Ind.FirstLine - X, Para.Pages[CurPage].X + ParaPr.Ind.Left - X);
|
||
}
|
||
|
||
X += NumberingItem.WidthVisible;
|
||
}
|
||
|
||
// Заполним обратные данные в элементе нумерации
|
||
NumberingItem.Item = Item;
|
||
NumberingItem.Run = Run;
|
||
NumberingItem.Line = CurLine;
|
||
NumberingItem.Range = CurRange;
|
||
NumberingItem.LineAscent = LineAscent;
|
||
NumberingItem.Page = CurPage;
|
||
|
||
return X;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsFast = function()
|
||
{
|
||
return this.Fast;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.AddFootnoteReference = function(oFootnoteReference, oPos)
|
||
{
|
||
// Ссылки могут добавляться несколько раз, если строка разбита на несколько отрезков
|
||
for (var nIndex = 0, nCount = this.Footnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (this.Footnotes[nIndex].FootnoteReference === oFootnoteReference)
|
||
return;
|
||
}
|
||
|
||
this.Footnotes.push({FootnoteReference : oFootnoteReference, Pos : oPos});
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetFootnoteReferencesCount = function(oFootnoteReference, isAllowCustom)
|
||
{
|
||
var _isAllowCustom = (true === isAllowCustom ? true : false);
|
||
|
||
// Если данную ссылку мы добавляли уже в строке, тогда ищем сколько было элементов до нее, если не добавляли,
|
||
// тогда возвращаем просто количество ссылок. Ссылки с флагом CustomMarkFollows не учитываются
|
||
|
||
var nRefsCount = 0;
|
||
for (var nIndex = 0, nCount = this.Footnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (this.Footnotes[nIndex].FootnoteReference === oFootnoteReference)
|
||
return nRefsCount;
|
||
|
||
if (true === _isAllowCustom || true !== this.Footnotes[nIndex].FootnoteReference.IsCustomMarkFollows())
|
||
nRefsCount++;
|
||
}
|
||
|
||
return nRefsCount;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.AddEndnoteReference = function(oEndnoteReference, oPos)
|
||
{
|
||
for (var nIndex = 0, nCount = this.Endnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (this.Endnotes[nIndex].EndnoteReference === oEndnoteReference)
|
||
return;
|
||
}
|
||
|
||
this.Endnotes.push({EndnoteReference : oEndnoteReference, Pos : oPos});
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetEndnoteReferenceNumber = function(oEndnoteReference)
|
||
{
|
||
if (this.Endnotes.length <= 0 || this.Endnotes[0].EndnoteReference === oEndnoteReference)
|
||
return -1;
|
||
|
||
var nRefsCount = 0;
|
||
for (var nIndex = 0, nCount = this.Endnotes.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (this.Endnotes[nIndex].EndnoteReference === oEndnoteReference)
|
||
return (this.Endnotes[0].EndnoteReference.Number + nRefsCount);
|
||
|
||
if (true !== this.Endnotes[nIndex].EndnoteReference.IsCustomMarkFollows())
|
||
nRefsCount++;
|
||
}
|
||
|
||
return (this.Endnotes[0].EndnoteReference.Number + nRefsCount);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetEndnoteReferenceCount = function()
|
||
{
|
||
return this.Endnotes.length;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.SetFast = function(bValue)
|
||
{
|
||
this.Fast = bValue;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsFastRecalculate = function()
|
||
{
|
||
return this.Fast;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.isFastRecalculation = function()
|
||
{
|
||
return this.Fast;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetPageAbs = function()
|
||
{
|
||
return this.PageAbs;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetColumnAbs = function()
|
||
{
|
||
return this.ColumnAbs;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetCurrentContentPos = function(nPos)
|
||
{
|
||
var oContentPos = this.CurPos.Copy();
|
||
oContentPos.Set(this.CurPos);
|
||
oContentPos.Add(nPos);
|
||
return oContentPos;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.SaveFootnotesInfo = function()
|
||
{
|
||
var oTopDocument = this.TopDocument;
|
||
if (oTopDocument instanceof CDocument)
|
||
this.FootnotesRecalculateObject = oTopDocument.Footnotes.SaveRecalculateObject(this.PageAbs, this.ColumnAbs);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.LoadFootnotesInfo = function()
|
||
{
|
||
var oTopDocument = this.TopDocument;
|
||
if (oTopDocument instanceof CDocument && this.FootnotesRecalculateObject)
|
||
oTopDocument.Footnotes.LoadRecalculateObject(this.PageAbs, this.ColumnAbs, this.FootnotesRecalculateObject);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsInTable = function()
|
||
{
|
||
return this.InTable;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetSectPr = function()
|
||
{
|
||
if (null === this.SectPr && this.Paragraph)
|
||
this.SectPr = this.Paragraph.Get_SectPr();
|
||
|
||
return this.SectPr;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetTopDocument = function()
|
||
{
|
||
return this.TopDocument;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetTopIndex = function()
|
||
{
|
||
if (-1 === this.TopIndex)
|
||
{
|
||
var arrPos = this.Paragraph.GetDocumentPositionFromObject();
|
||
if (arrPos.length > 0)
|
||
this.TopIndex = arrPos[0].Position;
|
||
}
|
||
|
||
return this.TopIndex;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.ResetMathRecalcInfo = function()
|
||
{
|
||
this.MathRecalcInfo.Line = 0;
|
||
this.MathRecalcInfo.Math = null;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.SetMathRecalcInfo = function(math)
|
||
{
|
||
this.MathRecalcInfo.Line = this.Line;
|
||
this.MathRecalcInfo.Math = math;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.resetToMathFirstLine = function()
|
||
{
|
||
this.Line = this.MathRecalcInfo.Line;
|
||
this.Y = this.LineY[this.Line];
|
||
return this.Line;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.GetMathRecalcInfoObject = function()
|
||
{
|
||
return this.MathRecalcInfo.Math;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.SetMathRecalcInfoObject = function(oMath)
|
||
{
|
||
this.MathRecalcInfo.Math = oMath;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsCondensedSpaces = function()
|
||
{
|
||
return this.CondensedSpaces;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsBalanceSingleByteDoubleByteWidth = function(oRun, nPos)
|
||
{
|
||
if (this.BalanceSBDB)
|
||
{
|
||
let oParaPos = this.Paragraph.GetPosByElement(oRun);
|
||
if (!oParaPos)
|
||
return true;
|
||
|
||
oParaPos.Add(nPos);
|
||
|
||
let oRunElements = new CParagraphRunElements(oParaPos, 1, null);
|
||
this.Paragraph.GetPrevRunElements(oRunElements);
|
||
let arrElements = oRunElements.GetElements();
|
||
if (arrElements.length <= 0)
|
||
return true;
|
||
|
||
let oItem = arrElements[0];
|
||
if (!oItem || para_Text !== oItem.Type || AscCommon.isEastAsianScript(oItem.Value))
|
||
return true;
|
||
|
||
oParaPos.Update(nPos + 1, oParaPos.GetDepth());
|
||
|
||
oRunElements = new CParagraphRunElements(oParaPos, 1, null);
|
||
this.Paragraph.GetNextRunElements(oRunElements);
|
||
arrElements = oRunElements.GetElements();
|
||
if (arrElements.length <= 0)
|
||
return true;
|
||
|
||
oItem = arrElements[0];
|
||
return (!oItem || para_Text !== oItem.Type || AscCommon.isEastAsianScript(oItem.Value));
|
||
}
|
||
|
||
return false;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.AddCondensedSpaceToRange = function(oSpace)
|
||
{
|
||
this.RangeSpaces.push(oSpace);
|
||
oSpace.ResetCondensedWidth();
|
||
};
|
||
/**
|
||
* Проверяем убирается ли в заданном отрезке заданная ширина содержимого
|
||
* @param x {number} - текущая позиция
|
||
* @param width {number} - ширина проверяемого промежутка
|
||
* @returns {boolean}
|
||
*/
|
||
CParagraphRecalculateStateWrap.prototype.isFitOnLine = function(x, width)
|
||
{
|
||
let xLimit = this.getXLimit();
|
||
if (x + width <= xLimit)
|
||
return true;
|
||
|
||
return this.tryCondenseSpaces(x, xLimit, width);
|
||
};
|
||
/**
|
||
* Пытаемся ужать пробелы по
|
||
* @param x {number} - текущая позиция
|
||
* @param xLimit {number} - предельная позиция
|
||
* @param width {number} - ширина проверяемого промежутка
|
||
* @returns {boolean}
|
||
*/
|
||
CParagraphRecalculateStateWrap.prototype.tryCondenseSpaces = function(x, xLimit, width)
|
||
{
|
||
if (!this.CondensedSpaces)
|
||
return false;
|
||
|
||
var nKoef = 1 - 0.25 * (Math.min(12.5, width) / 12.5);
|
||
|
||
var nSumSpaces = 0;
|
||
for (var nIndex = 0, nCount = this.RangeSpaces.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
nSumSpaces += this.RangeSpaces[nIndex].WidthOrigin / AscWord.TEXTWIDTH_DIVIDER;
|
||
}
|
||
|
||
var nSpace = nSumSpaces * (1 - nKoef);
|
||
if (x - nSpace + width < xLimit)
|
||
{
|
||
for (var nIndex = 0, nCount = this.RangeSpaces.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
this.RangeSpaces[nIndex].SetCondensedWidth(nKoef);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
for (var nIndex = 0, nCount = this.RangeSpaces.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
this.RangeSpaces[nIndex].ResetCondensedWidth();
|
||
}
|
||
}
|
||
|
||
return false;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.CheckUpdateLBP = function(nInRunPos)
|
||
{
|
||
if (this.UpdateLBP)
|
||
{
|
||
this.UpdateLBP = false;
|
||
this.LineBreakPos.Set(this.CurPos);
|
||
this.LineBreakPos.Add(nInRunPos);
|
||
}
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsNeedShapeFirstWord = function(nCurLine)
|
||
{
|
||
let arrLines = this.Paragraph.Lines;
|
||
|
||
return (0 !== nCurLine
|
||
&& arrLines.length > nCurLine
|
||
&& arrLines[nCurLine - 1].Info & paralineinfo_LongWord);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.IsLastElementInWord = function(oRun, nPos)
|
||
{
|
||
let oItem = oRun.GetElement(nPos)
|
||
if (!oItem)
|
||
return false;
|
||
|
||
if (oItem.IsSpaceAfter())
|
||
return true;
|
||
|
||
let oParent = oRun.GetParent();
|
||
let nInParentPos = oRun.GetPosInParent(oParent);
|
||
if (!oParent || -1 === nInParentPos)
|
||
return false;
|
||
|
||
let oNextItem = oRun.GetElement(nPos + 1);
|
||
let nParentLen = oParent.GetElementsCount();
|
||
while (!oNextItem && nInParentPos < nParentLen - 1)
|
||
{
|
||
oRun = oParent.GetElement(++nInParentPos);
|
||
if (!oRun || !(oRun instanceof ParaRun))
|
||
return true;
|
||
|
||
oNextItem = oRun.GetElement(0);
|
||
}
|
||
|
||
return (!oNextItem || !oNextItem.IsText());
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.isAutoHyphenation = function()
|
||
{
|
||
return this.autoHyphenation;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getAutoHyphenLimit = function()
|
||
{
|
||
return this.autoHyphenLimit;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.getHyphenationZone = function()
|
||
{
|
||
return this.hyphenationZone;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.onEndRecalculateLineRange = function()
|
||
{
|
||
// Сюда заходим, если закончили пересчиытывать отрезок насильно, а не из-за того, что какой-то элемент не убрался
|
||
// (перенос строк или конец параграфа)
|
||
this.ResetLastAutoHyphen();
|
||
};
|
||
/**
|
||
* Получам ширину дефиса, если на данном элементе можно разбить слово
|
||
* @returns {number}
|
||
*/
|
||
CParagraphRecalculateStateWrap.prototype.getAutoHyphenWidth = function(item, run)
|
||
{
|
||
if (!this.isAutoHyphenation() || !item || !item.IsText() || !item.isHyphenAfter())
|
||
return 0;
|
||
|
||
let textPr = run.Get_CompiledPr(false);
|
||
let fontInfo = textPr.GetFontInfo(AscWord.fontslot_ASCII);
|
||
return AscFonts.GetGraphemeWidth(AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(0x002D, fontInfo.Name, fontInfo.Style)) * textPr.FontSize;
|
||
};
|
||
/**
|
||
* Проверяем нужно ли сделать обязательный перенос строки после расчета одного диапазона
|
||
* @returns {boolean}
|
||
*/
|
||
CParagraphRecalculateStateWrap.prototype.isForceLineBreak = function()
|
||
{
|
||
return (this.ForceNewPage
|
||
|| this.ForceNewPageAfter
|
||
|| this.NewPage
|
||
|| this.ForceNewLine
|
||
|| this.LastHyphenItem);
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.isExceedConsecutiveAutoHyphenLimit = function()
|
||
{
|
||
let limit = this.getAutoHyphenLimit();
|
||
if (!limit)
|
||
return false;
|
||
|
||
let lines = this.Paragraph.Lines;
|
||
let curLine = this.Line - 1;
|
||
|
||
while (curLine >= 0 && lines[curLine].Info & paralineinfo_AutoHyphen)
|
||
--curLine;
|
||
|
||
++curLine;
|
||
|
||
return this.Line - curLine >= limit;
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.canPlaceAutoHyphenAfter = function(runItem)
|
||
{
|
||
return (this.isAutoHyphenation()
|
||
&& !this.isExceedConsecutiveAutoHyphenLimit()
|
||
&& runItem.isHyphenAfter());
|
||
};
|
||
CParagraphRecalculateStateWrap.prototype.checkHyphenationZone = function(x)
|
||
{
|
||
// Делаем как в MSWord (проверено в 2019 версии):
|
||
// отмеряем сколько уже занято на текущей строке от начала строки, добавляем это значение к левому полю документа
|
||
// и вычитаем из позиции правого поля параграфа. Если полученное значение больше hyphenationZone, значит можно
|
||
// делать перенос.
|
||
// Схема немного странная, т.к. мы считаем расстояние от левой границы параграфа, а добавляем его к левому полю,
|
||
// поэтому при смещении параграфа целиком влево или вправо (одинаковом изменении левого и правого отступов)
|
||
// разбиение может происходить по-разному, хотя ширина параграфа не меняется
|
||
|
||
let paraPr = this.Paragraph.Get_CompiledPr2(false).ParaPr;
|
||
|
||
let shift = paraPr.Ind.Left;
|
||
if (this.UseFirstLine)
|
||
shift += paraPr.Ind.FirstLine;
|
||
|
||
return x - shift < this.XLimit - this.getHyphenationZone();
|
||
};
|
||
AscWord.ParagraphRecalculationWrapState = CParagraphRecalculateStateWrap;
|
||
|
||
function CParagraphRecalculateStateCounter(wrapState)
|
||
{
|
||
this.wrapState = wrapState;
|
||
this.Paragraph = undefined;
|
||
this.Range = undefined;
|
||
this.Word = false;
|
||
this.SpaceLen = 0;
|
||
this.SpacesCount = 0;
|
||
|
||
this.Words = 0;
|
||
this.Spaces = 0;
|
||
this.Letters = 0;
|
||
this.SpacesSkip = 0;
|
||
this.LettersSkip = 0;
|
||
|
||
this.ComplexFields = new AscWord.ParagraphComplexFieldStack();
|
||
}
|
||
CParagraphRecalculateStateCounter.prototype.Reset = function(Paragraph, Range)
|
||
{
|
||
this.Paragraph = Paragraph;
|
||
this.Range = Range;
|
||
this.Word = false;
|
||
this.SpaceLen = 0;
|
||
this.SpacesCount = 0;
|
||
|
||
this.Words = 0;
|
||
this.Spaces = 0;
|
||
this.Letters = 0;
|
||
this.SpacesSkip = 0;
|
||
this.LettersSkip = 0;
|
||
|
||
this.ParaEnd = false;
|
||
this.LineBreak = false;
|
||
};
|
||
CParagraphRecalculateStateCounter.prototype.isFastRecalculation = function()
|
||
{
|
||
return this.wrapState.isFastRecalculation();
|
||
};
|
||
|
||
function CParagraphRecalculateStateAlign(wrapState)
|
||
{
|
||
this.wrapState = wrapState;
|
||
this.X = 0; // Текущая позиция по горизонтали
|
||
this.Y = 0; // Текущая позиция по вертикали
|
||
this.XEnd = 0; // Предельная позиция по горизонтали
|
||
this.JustifyWord = 0; // Добавочная ширина символов
|
||
this.JustifySpace = 0; // Добавочная ширина пробелов
|
||
this.SpacesCounter = 0; // Счетчик пробелов с добавочной шириной (чтобы пробелы в конце строки не трогать)
|
||
this.SpacesSkip = 0; // Количество пробелов, которые мы пропускаем в начале строки
|
||
this.LettersSkip = 0; // Количество букв, которые мы пропускаем (из-за таба)
|
||
this.LastW = 0; // Ширина последнего элемента (необходимо для позиционирования картинки)
|
||
this.Paragraph = undefined;
|
||
this.RecalcResult = 0x00;//recalcresult_NextElement;
|
||
|
||
this.Y0 = 0; // Верхняя граница строки
|
||
this.Y1 = 0; // Нижняя граница строки
|
||
|
||
this.CurPage = 0;
|
||
this.PageY = 0;
|
||
this.PageX = 0;
|
||
|
||
this.RecalcFast = false; // Если пересчет быстрый, тогда все "плавающие" объекты мы не трогаем
|
||
this.RecalcFast2 = false; // Второй вариант быстрого пересчета
|
||
|
||
this.ComplexFields = new AscWord.ParagraphComplexFieldStack();
|
||
}
|
||
CParagraphRecalculateStateAlign.prototype.IsFastRangeRecalc = function()
|
||
{
|
||
return this.RecalcFast;
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.getLogicDocument = function()
|
||
{
|
||
return this.wrapState.Paragraph.GetLogicDocument();
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.getDocumentSettings = function()
|
||
{
|
||
let logicDocument = this.Paragraph.GetLogicDocument();
|
||
if (logicDocument && logicDocument.IsDocumentEditor())
|
||
return logicDocument.getDocumentSettings();
|
||
|
||
return AscWord.DEFAULT_DOCUMENT_SETTINGS;
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.getCompatibilityMode = function()
|
||
{
|
||
return this.getDocumentSettings().getCompatibilityMode();
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.getLineTop = function()
|
||
{
|
||
let p = this.Paragraph;
|
||
return p.Pages[this.wrapState.Page].Y + p.Lines[this.wrapState.Line].Top;
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.getLineBottom = function()
|
||
{
|
||
let p = this.Paragraph;
|
||
return p.Pages[this.wrapState.Page].Y + p.Lines[this.wrapState.Line].Bottom;
|
||
};
|
||
CParagraphRecalculateStateAlign.prototype.isParagraphStartFromNewPage = function()
|
||
{
|
||
return this.Paragraph.IsStartFromNewPage();
|
||
};
|
||
|
||
|
||
function CParagraphRecalculateStateInfo()
|
||
{
|
||
ParagraphRecalculateStateBase.call(this);
|
||
this.fast = false;
|
||
this.Comments = [];
|
||
this.ComplexFields = [];
|
||
this.PermRanges = [];
|
||
}
|
||
CParagraphRecalculateStateInfo.prototype = Object.create(ParagraphRecalculateStateBase.prototype);
|
||
CParagraphRecalculateStateInfo.prototype.constructor = CParagraphRecalculateStateInfo;
|
||
CParagraphRecalculateStateInfo.prototype.setFast = function(isFast)
|
||
{
|
||
this.fast = isFast;
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.isFastRecalculation = function()
|
||
{
|
||
return this.fast;
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.Reset = function(prevInfo)
|
||
{
|
||
this.Comments = [];
|
||
this.ComplexFields = [];
|
||
this.PermRanges = [];
|
||
|
||
if (!prevInfo)
|
||
return;
|
||
|
||
if (prevInfo.Comments)
|
||
this.Comments = prevInfo.Comments.slice();
|
||
|
||
if (prevInfo.ComplexFields)
|
||
{
|
||
for (let index = 0, count = prevInfo.ComplexFields.length; index < count; ++index)
|
||
{
|
||
this.ComplexFields[index] = prevInfo.ComplexFields[index].Copy();
|
||
}
|
||
}
|
||
|
||
if (prevInfo.PermRanges)
|
||
this.PermRanges = prevInfo.PermRanges.slice();
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.AddComment = function(Id)
|
||
{
|
||
this.Comments.push(Id);
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.RemoveComment = function(Id)
|
||
{
|
||
var CommentsLen = this.Comments.length;
|
||
for (var CurPos = 0; CurPos < CommentsLen; CurPos++)
|
||
{
|
||
if (this.Comments[CurPos] === Id)
|
||
{
|
||
this.Comments.splice(CurPos, 1);
|
||
break;
|
||
}
|
||
}
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.addPermRange = function(rangeId)
|
||
{
|
||
this.PermRanges.push(rangeId);
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.removePermRange = function(rangeId)
|
||
{
|
||
let pos = this.PermRanges.indexOf(rangeId);
|
||
if (-1 === pos)
|
||
return;
|
||
|
||
if (this.PermRanges.length - 1 === pos)
|
||
--this.PermRanges.length;
|
||
else
|
||
this.PermRanges.splice(pos, 1);
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.processFieldChar = function(oFieldChar)
|
||
{
|
||
if (!oFieldChar || !oFieldChar.IsUse())
|
||
return;
|
||
|
||
var oComplexField = oFieldChar.GetComplexField();
|
||
|
||
if (oFieldChar.IsBegin())
|
||
{
|
||
this.ComplexFields.push(new CComplexFieldStatePos(oComplexField, true));
|
||
}
|
||
else if (oFieldChar.IsSeparate())
|
||
{
|
||
for (var nIndex = 0, nCount = this.ComplexFields.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (oComplexField === this.ComplexFields[nIndex].ComplexField)
|
||
{
|
||
this.ComplexFields[nIndex].SetFieldCode(false);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else if (oFieldChar.IsEnd())
|
||
{
|
||
for (var nIndex = 0, nCount = this.ComplexFields.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (oComplexField === this.ComplexFields[nIndex].ComplexField)
|
||
{
|
||
this.ComplexFields.splice(nIndex, 1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.isComplexField = function()
|
||
{
|
||
return (this.ComplexFields.length > 0 ? true : false);
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.isComplexFieldCode = function()
|
||
{
|
||
if (!this.isComplexField())
|
||
return false;
|
||
|
||
for (var nIndex = 0, nCount = this.ComplexFields.length; nIndex < nCount; ++nIndex)
|
||
{
|
||
if (this.ComplexFields[nIndex].IsFieldCode())
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.isHiddenComplexFieldPart = function()
|
||
{
|
||
for (let fieldIndex = 0, fieldCount = this.ComplexFields.length; fieldIndex < fieldCount; ++ fieldIndex)
|
||
{
|
||
let isFieldCode = this.ComplexFields[fieldIndex].IsFieldCode();
|
||
let isShowCode = this.ComplexFields[fieldIndex].IsShowFieldCode();
|
||
if (isFieldCode !== isShowCode)
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.processFieldCharAndCollectComplexField = function(oChar)
|
||
{
|
||
if (oChar.IsBegin())
|
||
{
|
||
var oComplexField = oChar.GetComplexField();
|
||
if (!oComplexField)
|
||
{
|
||
oChar.SetUse(false);
|
||
}
|
||
else
|
||
{
|
||
oChar.SetUse(true);
|
||
oComplexField.SetBeginChar(oChar);
|
||
this.ComplexFields.push(new CComplexFieldStatePos(oComplexField, true));
|
||
}
|
||
}
|
||
else if (oChar.IsEnd())
|
||
{
|
||
if (this.ComplexFields.length > 0)
|
||
{
|
||
oChar.SetUse(true);
|
||
var oComplexField = this.ComplexFields[this.ComplexFields.length - 1].ComplexField;
|
||
oComplexField.SetEndChar(oChar);
|
||
this.ComplexFields.splice(this.ComplexFields.length - 1, 1);
|
||
|
||
if (this.ComplexFields.length > 0 && this.ComplexFields[this.ComplexFields.length - 1].IsFieldCode())
|
||
this.ComplexFields[this.ComplexFields.length - 1].ComplexField.SetInstructionCF(oComplexField);
|
||
}
|
||
else
|
||
{
|
||
oChar.SetUse(false);
|
||
}
|
||
}
|
||
else if (oChar.IsSeparate())
|
||
{
|
||
if (this.ComplexFields.length > 0)
|
||
{
|
||
oChar.SetUse(true);
|
||
var oComplexField = this.ComplexFields[this.ComplexFields.length - 1].ComplexField;
|
||
oComplexField.SetSeparateChar(oChar);
|
||
this.ComplexFields[this.ComplexFields.length - 1].SetFieldCode(false);
|
||
}
|
||
else
|
||
{
|
||
oChar.SetUse(false);
|
||
}
|
||
}
|
||
};
|
||
CParagraphRecalculateStateInfo.prototype.processInstruction = function(oInstruction)
|
||
{
|
||
if (this.ComplexFields.length <= 0)
|
||
return;
|
||
|
||
var oComplexField = this.ComplexFields[this.ComplexFields.length - 1].ComplexField;
|
||
if (oComplexField && null === oComplexField.GetSeparateChar())
|
||
oComplexField.SetInstruction(oInstruction);
|
||
};
|
||
|
||
const g_PRSI = new CParagraphRecalculateStateInfo();
|
||
|
||
function CParagraphRecalculateObject()
|
||
{
|
||
this.X = 0;
|
||
this.Y = 0;
|
||
this.XLimit = 0;
|
||
this.YLimit = 0;
|
||
|
||
this.Pages = [];
|
||
this.Lines = [];
|
||
this.Content = [];
|
||
}
|
||
|
||
CParagraphRecalculateObject.prototype =
|
||
{
|
||
Save : function(Para)
|
||
{
|
||
this.X = Para.X;
|
||
this.Y = Para.Y;
|
||
this.XLimit = Para.XLimit;
|
||
this.YLimit = Para.YLimit;
|
||
|
||
this.Pages = Para.Pages;
|
||
this.Lines = Para.Lines;
|
||
|
||
var Content = Para.Content;
|
||
var Count = Content.length;
|
||
for ( var Index = 0; Index < Count; Index++ )
|
||
{
|
||
this.Content[Index] = Content[Index].SaveRecalculateObject();
|
||
}
|
||
},
|
||
|
||
Load : function(Para)
|
||
{
|
||
Para.X = this.X;
|
||
Para.Y = this.Y;
|
||
Para.XLimit = this.XLimit;
|
||
Para.YLimit = this.YLimit;
|
||
|
||
Para.Pages = this.Pages;
|
||
Para.Lines = this.Lines;
|
||
|
||
var Count = Para.Content.length;
|
||
for ( var Index = 0; Index < Count; Index++ )
|
||
{
|
||
Para.Content[Index].LoadRecalculateObject(this.Content[Index], Para);
|
||
}
|
||
},
|
||
|
||
Get_DrawingFlowPos : function(FlowPos)
|
||
{
|
||
var Count = this.Content.length;
|
||
for ( var Index = 0; Index < Count; Index++ )
|
||
{
|
||
this.Content[Index].Get_DrawingFlowPos( FlowPos );
|
||
}
|
||
}
|
||
};
|