Files
DocumentServer-v-9.2.0/sdkjs/word/Editor/Paragraph_Recalculate.js
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

4748 lines
177 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (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 );
}
}
};