295 lines
9.4 KiB
JavaScript
295 lines
9.4 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)
|
||
{
|
||
/**
|
||
* @constructor
|
||
*/
|
||
function CTextFontInfo(sName, nStyle, nSize)
|
||
{
|
||
this.Name = undefined !== sName ? sName : "Arial";
|
||
this.Style = undefined !== nStyle ? nStyle : 0;
|
||
this.Size = undefined !== nSize ? nSize : 10;
|
||
}
|
||
|
||
const DEFAULT_TEXTFONTINFO = new CTextFontInfo();
|
||
|
||
// Функции для возможной перегрузки
|
||
// 1. FlushGrapheme - основная функция, которую нужно ОБЯЗАТЕЛЬНО реализовывать в дочернем классе
|
||
// 2. Shape - простая функция для шейпинга текста
|
||
// 3. GetFontInfo - получаем информацию о шрифте на момент составления графем
|
||
// 4. GetCodePoint - данная функция нужна, чтобы в буфере можно было хранить не сам юникод, а его содержащий класс
|
||
// 5. GetLigaturesType - какие лигатуры поддерживать
|
||
|
||
/**
|
||
* @constructor
|
||
*/
|
||
function CTextShaper()
|
||
{
|
||
this.Buffer = [];
|
||
this.BufferIndex = 0;
|
||
this.Script = -1;
|
||
this.FontId = -1;
|
||
this.FontSubst = false;
|
||
this.FontSlot = AscWord.fontslot_None;
|
||
this.FontSize = 10;
|
||
this.ForceCheckFont = false;
|
||
this.Direction = AscFonts.HB_DIRECTION.HB_DIRECTION_LTR;
|
||
}
|
||
CTextShaper.prototype.ClearBuffer = function()
|
||
{
|
||
this.Buffer.length = 0;
|
||
this.BufferIndex = 0;
|
||
|
||
this.Script = -1;
|
||
this.FontId = -1;
|
||
this.FontSlot = AscWord.fontslot_None;
|
||
this.Direction = AscFonts.HB_DIRECTION.HB_DIRECTION_LTR;
|
||
|
||
this.StartString();
|
||
};
|
||
CTextShaper.prototype.ResetBuffer = function()
|
||
{
|
||
if (this.IsRtlDirection())
|
||
this.BufferIndex = this.Buffer.length;
|
||
};
|
||
CTextShaper.prototype.StartString = function()
|
||
{
|
||
AscFonts.HB_StartString();
|
||
};
|
||
CTextShaper.prototype.AppendToString = function(oItem)
|
||
{
|
||
let nCodePoint = this.GetCodePoint(oItem);
|
||
nCodePoint = this.private_CheckNewSegment(nCodePoint);
|
||
this.Buffer.push(oItem);
|
||
AscFonts.HB_AppendToString(nCodePoint);
|
||
};
|
||
CTextShaper.prototype.EndString = function()
|
||
{
|
||
AscFonts.HB_EndString();
|
||
};
|
||
CTextShaper.prototype.FlushWord = function()
|
||
{
|
||
if (this.Buffer.length <= 0)
|
||
return this.ClearBuffer();
|
||
|
||
this.EndString();
|
||
|
||
let nScript = AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED === this.Script ? AscFonts.HB_SCRIPT.HB_SCRIPT_COMMON : this.Script;
|
||
|
||
this.Direction = this.GetDirection(nScript);
|
||
this.ResetBuffer();
|
||
|
||
let oFontInfo = this.GetFontInfo(this.FontSlot);
|
||
let nFontId = AscCommon.FontNameMap.GetId(this.FontId.m_pFaceInfo.family_name);
|
||
AscCommon.g_oTextMeasurer.SetFontInternal(this.FontId.m_pFaceInfo.family_name, AscFonts.MEASURE_FONTSIZE, oFontInfo.Style);
|
||
|
||
this.FontSize = oFontInfo.Size;
|
||
|
||
AscFonts.HB_ShapeString(this, nFontId, oFontInfo.Style, this.FontId, this.GetLigaturesType(nScript), nScript, this.Direction, "en");
|
||
|
||
// Значит шрифт был подобран, возвращаем назад состояние отрисовщика
|
||
if (this.FontId.m_pFaceInfo.family_name !== oFontInfo.Name)
|
||
{
|
||
AscCommon.g_oTextMeasurer.SetFontInternal(oFontInfo.Name, AscFonts.MEASURE_FONTSIZE, oFontInfo.Style);
|
||
this.ForceCheckFont = true;
|
||
}
|
||
|
||
this.ClearBuffer();
|
||
};
|
||
CTextShaper.prototype.GetLigaturesType = function(textScript)
|
||
{
|
||
return Asc.LigaturesType.None;
|
||
};
|
||
CTextShaper.prototype.GetTextScript = function(nUnicode)
|
||
{
|
||
return AscFonts.hb_get_script_by_unicode(nUnicode);
|
||
};
|
||
CTextShaper.prototype.GetFontSlot = function(nUnicode)
|
||
{
|
||
return AscWord.GetFontSlot(nUnicode, AscWord.fonthint_Default, lcid_unknown, false, false);
|
||
};
|
||
CTextShaper.prototype.GetDirection = function(nScript)
|
||
{
|
||
return AscFonts.hb_get_script_horizontal_direction(nScript);
|
||
};
|
||
CTextShaper.prototype.private_CheckNewSegment = function(nUnicode)
|
||
{
|
||
if (this.Buffer.length >= AscFonts.HB_STRING_MAX_LEN)
|
||
this.FlushWord();
|
||
|
||
// TODO: Check bugs 66317 66435
|
||
let nScript = this.GetTextScript(nUnicode);
|
||
if (nScript !== this.Script
|
||
&& -1 !== this.Script
|
||
&& AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED !== nScript
|
||
&& AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED !== this.Script)
|
||
this.FlushWord();
|
||
|
||
if (this.GetDirection(this.Script) !== this.GetDirection(nScript))
|
||
this.FlushWord();
|
||
|
||
let nFontSlot = this.GetFontSlot(nUnicode);
|
||
this.private_CheckFont(nFontSlot);
|
||
|
||
let nFontId = this.FontId;
|
||
let isSubst;
|
||
if (AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED === nScript && this.FontSubst)
|
||
{
|
||
let oInfo = AscCommon.g_oTextMeasurer.GetFontBySymbol(nUnicode, -1 !== nFontId ? nFontId : null, true);
|
||
nFontId = oInfo.Font;
|
||
nUnicode = oInfo.CodePoint;
|
||
isSubst = true;
|
||
}
|
||
else
|
||
{
|
||
isSubst = !AscCommon.g_oTextMeasurer.CheckUnicodeInCurrentFont(nUnicode);
|
||
if (-1 === nFontId || isSubst)
|
||
{
|
||
let oInfo = AscCommon.g_oTextMeasurer.GetFontBySymbol(nUnicode, -1 !== nFontId ? nFontId : null);
|
||
nFontId = oInfo.Font;
|
||
nUnicode = oInfo.CodePoint;
|
||
}
|
||
else
|
||
{
|
||
let nCurFontId = AscCommon.g_oTextMeasurer.GetCurrentFont();
|
||
if (nCurFontId)
|
||
nFontId = nCurFontId;
|
||
}
|
||
}
|
||
|
||
if (this.FontId !== nFontId
|
||
&& -1 !== this.FontId
|
||
&& this.FontSubst
|
||
&& isSubst
|
||
&& this.private_CheckBufferInFont(nFontId))
|
||
this.FontId = nFontId;
|
||
|
||
if (this.FontId !== nFontId && -1 !== this.FontId)
|
||
this.FlushWord();
|
||
|
||
this.Script = AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED !== nScript || -1 === this.Script ? nScript : this.Script;
|
||
this.FontSlot = AscWord.fontslot_None !== nFontSlot ? nFontSlot : this.FontSlot;
|
||
this.FontId = nFontId;
|
||
this.FontSubst = isSubst;
|
||
|
||
return nUnicode;
|
||
};
|
||
CTextShaper.prototype.private_CheckFont = function(nFontSlot)
|
||
{
|
||
if (this.FontSlot === nFontSlot && !this.ForceCheckFont)
|
||
return;
|
||
|
||
let oFontInfo = this.GetFontInfo(nFontSlot);
|
||
AscCommon.g_oTextMeasurer.SetFontInternal(oFontInfo.Name, AscFonts.MEASURE_FONTSIZE, oFontInfo.Style);
|
||
this.ForceCheckFont = false;
|
||
};
|
||
CTextShaper.prototype.private_CheckBufferInFont = function(nFontId)
|
||
{
|
||
for (let nPos = 0, nCount = this.Buffer.length; nPos < nCount; ++nPos)
|
||
{
|
||
if (!nFontId.GetGIDByUnicode(this.GetCodePoint(this.Buffer[nPos])))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
CTextShaper.prototype.GetFontInfo = function(nFontSlot)
|
||
{
|
||
return DEFAULT_TEXTFONTINFO;
|
||
};
|
||
CTextShaper.prototype.Shape = function(sString)
|
||
{
|
||
for (var oIterator = sString.getUnicodeIterator(); oIterator.check(); oIterator.next())
|
||
{
|
||
let nCodePoint = oIterator.value();
|
||
this.AppendToString(nCodePoint);
|
||
}
|
||
this.FlushWord();
|
||
};
|
||
CTextShaper.prototype.GetCodePoint = function(oItem)
|
||
{
|
||
return oItem;
|
||
};
|
||
CTextShaper.prototype.FlushGrapheme = function(nGrapheme, nWidth, nCodePointsCount, isLigature)
|
||
{
|
||
if (this.IsRtlDirection())
|
||
this.BufferIndex -= nCodePointsCount;
|
||
else
|
||
this.BufferIndex += nCodePointsCount;
|
||
};
|
||
CTextShaper.prototype.IsRtlDirection = function()
|
||
{
|
||
return this.Direction === AscFonts.HB_DIRECTION.HB_DIRECTION_RTL;
|
||
};
|
||
CTextShaper.prototype.PrintAllUnicodesByScript = function(nScript)
|
||
{
|
||
let log = "";
|
||
|
||
function Flush(isForce)
|
||
{
|
||
if (log.length > 100 || isForce)
|
||
{
|
||
console.log(log);
|
||
log = "";
|
||
}
|
||
}
|
||
|
||
for (let u = 0; u < 0x10FFFF; ++u)
|
||
{
|
||
if (nScript === this.GetTextScript(u))
|
||
{
|
||
log += AscCommon.IntToHex(u) + " " + String.fromCodePoint(u) + " ";
|
||
Flush(false);
|
||
}
|
||
}
|
||
|
||
Flush(true);
|
||
};
|
||
|
||
function isRtlScript(unicode)
|
||
{
|
||
return AscFonts.hb_get_script_horizontal_direction(AscFonts.hb_get_script_by_unicode(unicode)) === AscFonts.HB_DIRECTION.HB_DIRECTION_RTL;
|
||
}
|
||
|
||
//--------------------------------------------------------export----------------------------------------------------
|
||
window['AscFonts'] = window['AscFonts'] || {};
|
||
window['AscFonts'].CTextFontInfo = CTextFontInfo;
|
||
window['AscFonts'].CTextShaper = CTextShaper;
|
||
window['AscFonts'].DEFAULT_TEXTFONTINFO = DEFAULT_TEXTFONTINFO;
|
||
window['AscFonts'].isRtlScript = isRtlScript;
|
||
|
||
})(window);
|