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);
|