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

386 lines
13 KiB
JavaScript

/*
* (c) Copyright Ascensio System SIA 2010-2024
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
"use strict";
(function(window)
{
const CODEPOINT_TYPE = {
BASE : 0,
LIGATURE : 1,
LIGATURE_CONTINUE : 2,
COMBINING_MARK : 3
};
/**
*
* @constructor
*/
function CParagraphTextShaper()
{
AscFonts.CTextShaper.call(this);
this.Parent = null;
this.Paragraph = null;
this.TextPr = null;
this.Temporary = false;
this.Ligatures = Asc.LigaturesType.None;
this.Spacing = 0;
this.AscFont = false; // Специальный случай, когда используемый шрифт ASCW3, а не тот, что задан в настройках
this.MaskSymbol = null; // Символ, который используется для маскирования текста в полях ввода
}
CParagraphTextShaper.prototype = Object.create(AscFonts.CTextShaper.prototype);
CParagraphTextShaper.prototype.constructor = CParagraphTextShaper;
CParagraphTextShaper.prototype.Init = function(isTemporary)
{
this.Parent = null;
this.Paragraph = null;
this.TextPr = null;
this.Temporary = isTemporary;
this.Ligatures = Asc.LigaturesType.None;
this.Spacing = 0;
this.AscFont = false;
this.ClearBuffer();
};
CParagraphTextShaper.prototype.GetCodePoint = function(oItem)
{
if (null !== this.MaskSymbol)
return this.MaskSymbol;
let nCodePoint = oItem.GetCodePoint();
if (this.TextPr && (this.TextPr.Caps || this.TextPr.SmallCaps))
nCodePoint = (String.fromCharCode(nCodePoint).toUpperCase()).charCodeAt(0);
return nCodePoint;
};
CParagraphTextShaper.prototype.GetFontInfo = function(nFontSlot)
{
if (!this.TextPr)
return AscFonts.DEFAULT_TEXTFONTINFO;
let oFontInfo = this.TextPr.GetFontInfo(nFontSlot);
if (this.AscFont)
oFontInfo.Name = "ASCW3";
return oFontInfo;
};
CParagraphTextShaper.prototype.GetFontSlot = function(nUnicode)
{
let oTextPr = this.TextPr;
if (!oTextPr)
return AscWord.fontslot_None;
return AscWord.GetFontSlotByTextPr(nUnicode, oTextPr);
};
CParagraphTextShaper.prototype.GetLigaturesType = function(textScript)
{
// bug-73560
let result = this.Ligatures;
if (AscFonts.HB_SCRIPT.HB_SCRIPT_ARABIC === textScript)
result |= Asc.LigaturesType.Standard;
return result;
};
CParagraphTextShaper.prototype.Shape = function(oParagraph)
{
this.Init(false);
let oThis = this;
oParagraph.CheckRunContent(function(oRun, nStartPos, nEndPos)
{
oThis.HandleRun(oRun, nStartPos, nEndPos);
});
this.FlushWord();
};
CParagraphTextShaper.prototype.ShapeRange = function(oParagraph, oStart, oEnd, isTemporary)
{
this.Init(isTemporary);
let oThis = this;
oParagraph.CheckRunContent(function(oRun, nStartPos, nEndPos)
{
oThis.HandleRun(oRun, nStartPos, nEndPos);
}, oStart, oEnd);
this.FlushWord();
};
CParagraphTextShaper.prototype.ShapeRun = function(run)
{
this.Init(false);
this.HandleRun(run, 0, run.GetElementsCount());
this.FlushWord();
};
CParagraphTextShaper.prototype.HandleRun = function(oRun, nStartPos, nEndPos)
{
this.private_CheckRun(oRun);
for (let nPos = nStartPos; nPos < nEndPos; ++nPos)
{
let oItem = oRun.GetElement(nPos);
if (!oItem.IsText())
{
this.FlushWord();
if (oItem.IsSpace())
this.private_HandleSpace(oItem);
}
else if (oItem.IsNBSP())
{
this.FlushWord();
this.private_HandleNBSP(oItem);
}
else if (oItem.IsDigit() && this.private_IsReplaceToHindiDigits())
{
this.FlushWord();
this.private_HandleHindiDigit(oItem);
}
else
{
this.AppendToString(oItem);
if (oItem.IsSpaceAfter())
this.FlushWord();
}
}
};
CParagraphTextShaper.prototype.FlushGrapheme = function(nGrapheme, nWidth, nCodePointsCount, isLigature)
{
if (nCodePointsCount <= 0)
return;
let curIndex = 0;
if (this.IsRtlDirection())
{
if (this.BufferIndex - nCodePointsCount < 0)
return;
this.BufferIndex -= nCodePointsCount;
curIndex = this.BufferIndex;
}
else
{
if (this.BufferIndex + nCodePointsCount - 1 >= this.Buffer.length)
return;
curIndex = this.BufferIndex;
this.BufferIndex += nCodePointsCount;
}
let _nWidth = (nWidth + (this.Spacing / this.FontSize)) / nCodePointsCount;
if (1 === nCodePointsCount)
{
this.private_HandleItem(this.Buffer[curIndex], nGrapheme, _nWidth, this.FontSize, this.FontSlot, CODEPOINT_TYPE.BASE);
}
else
{
if (this.IsRtlDirection())
{
this.private_HandleItem(this.Buffer[curIndex], AscFonts.NO_GRAPHEME, _nWidth, this.FontSize, this.FontSlot, isLigature ? CODEPOINT_TYPE.LIGATURE : CODEPOINT_TYPE.BASE);
this.private_HandleItem(this.Buffer[curIndex + nCodePointsCount - 1], nGrapheme, _nWidth, this.FontSize, this.FontSlot, isLigature ? CODEPOINT_TYPE.LIGATURE_CONTINUE : CODEPOINT_TYPE.COMBINING_MARK);
}
else
{
this.private_HandleItem(this.Buffer[curIndex], nGrapheme, _nWidth, this.FontSize, this.FontSlot, isLigature ? CODEPOINT_TYPE.LIGATURE : CODEPOINT_TYPE.BASE);
this.private_HandleItem(this.Buffer[curIndex + nCodePointsCount - 1], AscFonts.NO_GRAPHEME, _nWidth, this.FontSize, this.FontSlot, isLigature ? CODEPOINT_TYPE.LIGATURE_CONTINUE : CODEPOINT_TYPE.COMBINING_MARK);
}
for (let nIndex = 1; nIndex < nCodePointsCount - 1; ++nIndex)
{
this.private_HandleItem(this.Buffer[++curIndex], AscFonts.NO_GRAPHEME, _nWidth, this.FontSize, AscWord.fontslot_ASCII, isLigature ? CODEPOINT_TYPE.LIGATURE_CONTINUE : CODEPOINT_TYPE.COMBINING_MARK);
}
}
};
CParagraphTextShaper.prototype.private_CheckRun = function(oRun)
{
let oRunParent = oRun.GetParent();
let oTextPr = oRun.Get_CompiledPr(false);
if (this.Parent !== oRunParent || !this.IsEqualTextPr(oTextPr))
this.FlushWord();
let oForm = oRun.GetParentForm();
let isCombForm = oForm && oForm.IsTextForm() && oForm.GetTextFormPr().IsComb();
this.Paragraph = oRun.GetParagraph();
this.Parent = oRunParent;
this.TextPr = oTextPr;
this.Spacing = isCombForm ? 0 : oTextPr.Spacing;
this.Ligatures = isCombForm || Math.abs(this.Spacing) > 0.001 ? Asc.LigaturesType.None : oTextPr.Ligatures;
this.AscFont = oRun.IsUseAscFont(oTextPr);
};
CParagraphTextShaper.prototype.private_HandleNBSP = function(oItem)
{
let oFontInfo = this.TextPr.GetFontInfo(AscWord.fontslot_ASCII);
let nGrapheme = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(0x00B0, oFontInfo.Name, oFontInfo.Style);
let nSpace = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(0x0020, oFontInfo.Name, oFontInfo.Style);
this.private_HandleItem(oItem, nGrapheme, AscFonts.GetGraphemeWidth(nSpace), oFontInfo.Size, AscWord.fontslot_ASCII, false, false, false);
};
CParagraphTextShaper.prototype.private_HandleItem = function(oItem, nGrapheme, nWidth, nFontSize, nFontSlot, nCodePointType)
{
if (this.Temporary)
{
oItem.ResetTemporaryGrapheme();
if (nGrapheme !== oItem.GetGrapheme()
|| nCodePointType !== oItem.GetCodePointType()
|| Math.abs(nWidth - oItem.GetMeasuredWidth()) > 0.001)
{
oItem.SetTemporaryGrapheme(nGrapheme);
oItem.SetTemporaryCodePointType(nCodePointType);
oItem.SetTemporaryWidth(nWidth);
}
}
else
{
oItem.SetGrapheme(nGrapheme);
oItem.SetMetrics(nFontSize, nFontSlot, this.TextPr);
oItem.SetCodePointType(nCodePointType);
oItem.SetWidth(nWidth);
}
};
CParagraphTextShaper.prototype.private_HandleSpace = function(item)
{
let codePoint = this.MaskSymbol ? this.MaskSymbol : item.GetCodePoint();
let fontInfo = this.TextPr.GetFontInfo(AscWord.fontslot_ASCII);
let grapheme = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(codePoint, fontInfo.Name, fontInfo.Style);
let enGrapheme = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(0x2002, fontInfo.Name, fontInfo.Style);
let width = AscFonts.GetGraphemeWidth(grapheme);
let enWidth = (AscFonts.NO_GRAPHEME === enGrapheme ? 25.4 / 72 / 2 : AscFonts.GetGraphemeWidth(enGrapheme));
item.SetGrapheme(this.MaskSymbol ? grapheme : AscFonts.NO_GRAPHEME);
item.SetMetrics(fontInfo.Size, AscWord.fontslot_ASCII, this.TextPr);
item.SetWidth(width, this.TextPr, enWidth);
};
CParagraphTextShaper.prototype.IsEqualTextPr = function(oTextPr)
{
// Здесь мы не используем стандартную функцию CTextPr.IsEqual, потому что она сравнивает на полное
// совпадение объектов, а нас интересует только совпадение настроек, которые не мешают сборке текста
if (!oTextPr || !this.TextPr)
return false;
let t = this.TextPr;
return (t.Bold === oTextPr.Bold
&& t.BoldCS === oTextPr.BoldCS
&& t.Italic === oTextPr.Italic
&& t.ItalicCS === oTextPr.ItalicCS
&& IsEqualNullableFloatNumbers(t.FontSize, oTextPr.FontSize)
&& IsEqualNullableFloatNumbers(t.FontSizeCS, oTextPr.FontSizeCS)
&& t.VertAlign === oTextPr.VertAlign
&& IsEqualNullableFloatNumbers(t.Spacing, oTextPr.Spacing)
&& t.SmallCaps === oTextPr.SmallCaps
&& t.Position === oTextPr.Position
&& t.CS === oTextPr.CS
&& t.RTL === oTextPr.RTL
&& t.Vanish === oTextPr.Vanish
&& t.Ligatures === oTextPr.Ligatures
&& t.RFonts.IsEqualSlot(oTextPr.RFonts, this.FontSlot));
};
CParagraphTextShaper.prototype.GetTextScript = function(nUnicode)
{
// TODO: Remove it after implementing bigi algorithm
// Check bugs 66317, 66435
if (0x060C <= nUnicode && nUnicode <= 0x074A)
return AscFonts.HB_SCRIPT.HB_SCRIPT_ARABIC;
let script = AscFonts.hb_get_script_by_unicode(nUnicode);
if (AscFonts.HB_SCRIPT.HB_SCRIPT_COMMON === script && this.TextPr && this.TextPr.CS)
return AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED;
return script;
};
CParagraphTextShaper.prototype.ShapeRunTextItem = function(item, textPr)
{
let fontSlot = item.GetFontSlot(textPr);
let fontInfo = textPr.GetFontInfo(fontSlot);
let grapheme = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(item.GetCodePoint(), fontInfo.Name, fontInfo.Style);
item.SetGrapheme(grapheme);
item.SetMetrics(fontInfo.Size, fontSlot, textPr);
item.SetCodePointType(CODEPOINT_TYPE.BASE);
item.SetWidth(AscFonts.GetGraphemeWidth(grapheme));
};
CParagraphTextShaper.prototype.private_IsReplaceToHindiDigits = function()
{
if (this.MaskSymbol)
return false;
if (Asc.editor.isPdfEditor())
{
let oParent = this.Paragraph.GetParent();
if (oParent.ParentPDF && oParent.ParentPDF.IsForm())
return oParent.ParentPDF.IsHindiDigits();
return false;
}
let logicDocument = this.Paragraph ? this.Paragraph.GetLogicDocument() : undefined;
return (logicDocument
&& logicDocument.IsDocumentEditor()
&& Asc.c_oNumeralType.hindi === logicDocument.GetNumeralType());
};
CParagraphTextShaper.prototype.private_HandleHindiDigit = function(oItem)
{
let oFontInfo = this.TextPr.GetFontInfo(AscWord.fontslot_ASCII);
let nGrapheme = AscCommon.g_oTextMeasurer.GetGraphemeByUnicode(oItem.GetCodePoint() + (0x0660 - 0x0030), oFontInfo.Name, oFontInfo.Style);
this.private_HandleItem(oItem, nGrapheme, AscFonts.GetGraphemeWidth(nGrapheme), oFontInfo.Size, AscWord.fontslot_ASCII, false, false, false);
};
CParagraphTextShaper.prototype.SetMaskSymbol = function(maskSymbol)
{
if (typeof maskSymbol === 'string')
this.MaskSymbol = maskSymbol.codePointAt(0);
else if (!isNaN(maskSymbol))
this.MaskSymbol = maskSymbol;
else
this.MaskSymbol = null;
};
CParagraphTextShaper.prototype.GetMaskSymbol = function()
{
if (typeof maskSymbol === 'string')
this.MaskSymbol = maskSymbol.codePointAt(0);
else if (!isNaN(maskSymbol))
this.MaskSymbol = maskSymbol;
else
this.MaskSymbol = null;
};
//--------------------------------------------------------export----------------------------------------------------
window['AscWord'] = window['AscWord'] || {};
window['AscWord'].CODEPOINT_TYPE = CODEPOINT_TYPE;
window['AscWord'].ParagraphTextShaper = new CParagraphTextShaper();
window['AscWord'].stringShaper = new AscFonts.StringShaper();
})(window);