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