Files
core/DesktopEditor/doctrenderer/embed/TextMeasurerEmbed.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

310 lines
11 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* 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, together with the
* additional terms provided in the LICENSE file.
*
* 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: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#include "./TextMeasurerEmbed.h"
#include "./PointerEmbed.h"
#include "./../../fontengine/TextShaper.h"
#include "./../../fontengine/TextHyphen.h"
#define RAW_POINTER(value) ((CPointerEmbedObject*)value->toObject()->getNative())->Data
#define POINTER_OBJECT(value) ((CPointerEmbedObject*)value->toObject()->getNative())
// in js we don't want to track that each face has a reference to library - i.e. that
// all faces are deleted first and then library - so we make our own reference counter
// and track library ourselves. I.e. we use FT_Library_Reference/FT_Library_UnReference
class CExternalPointerJS : public NSShaper::CExternalPointer
{
public:
CExternalPointerJS() : NSShaper::CExternalPointer() {}
virtual ~CExternalPointerJS() {}
public:
virtual void Alloc(const unsigned int& len)
{
Len = len;
Data = NSAllocator::Alloc((size_t)Len);
}
virtual void Free()
{
if (Data)
NSAllocator::Free(Data, (size_t)Len);
}
};
CTextMeasurerEmbed::CTextMeasurerEmbed()
{
m_hyphen_engine = new NSHyphen::CEngine();
}
CTextMeasurerEmbed::~CTextMeasurerEmbed()
{
NSHyphen::CEngine* tmp = (NSHyphen::CEngine*)m_hyphen_engine;
delete tmp;
m_hyphen_engine = NULL;
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Malloc(JSSmart<CJSValue> typed_array_or_len)
{
void* pData = NULL;
size_t len = 0;
if (typed_array_or_len->isNumber())
{
len = (size_t)typed_array_or_len->toInt32();
if (0 != len)
pData = malloc((size_t)len);
}
else
{
JSSmart<CJSTypedArray> typedArray = typed_array_or_len->toTypedArray();
CJSDataBuffer pBuffer = typedArray->getData();
pData = malloc(pBuffer.Len);
memcpy(pData, pBuffer.Data, pBuffer.Len);
if (pBuffer.IsExternalize)
pBuffer.Free();
}
CPointerEmbedObject* pObject = new CPointerEmbedObject(pData, NSPointerObjectDeleters::FreeDeleter);
return pObject->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Free(JSSmart<CJSValue> pointer)
{
CPointerEmbedObject* pEmbed = (CPointerEmbedObject*)pointer->toObject()->getNative();
pEmbed->Free();
return CJSContext::createUndefined();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Init()
{
CPointerEmbedObject* pointer = new CPointerEmbedObject(NSShaper::FT_Library_Init(), [](void* data) { NSShaper::FT_Library_UnReference(data); });
return pointer->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Set_TrueType_HintProp(JSSmart<CJSValue> library, JSSmart<CJSValue> tt_interpreter)
{
return CJSContext::createInt(NSShaper::FT_Set_TrueType_HintProp(RAW_POINTER(library), tt_interpreter->toUInt32()));
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Open_Face(JSSmart<CJSValue> library, JSSmart<CJSValue> memory, JSSmart<CJSValue> size, JSSmart<CJSValue> face_index)
{
void* ftlibrary = RAW_POINTER(library);
void* face = NSShaper::FT_Open_Face(ftlibrary, (unsigned char*)RAW_POINTER(memory), size->toUInt32(), face_index->toInt32());
if (NULL != face)
NSShaper::FT_Library_Reference(ftlibrary);
CPointerEmbedObject* pointer = new CPointerEmbedObject(face, [](void* data) { NSShaper::FT_Done_Face_With_Library(data); });
return pointer->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Open_Face2(JSSmart<CJSValue> library, JSSmart<CJSValue> array, JSSmart<CJSValue> face_index)
{
void* ftlibrary = RAW_POINTER(library);
JSSmart<CJSTypedArray> typedArray = array->toTypedArray();
CJSDataBuffer buffer = typedArray->getData();
void* face = NSShaper::FT_Open_Face(ftlibrary, (unsigned char*)buffer.Data, (unsigned int)buffer.Len, face_index->toInt32());
if (NULL != face)
NSShaper::FT_Library_Reference(ftlibrary);
CPointerEmbedObject* pointer = new CPointerEmbedObject(face, [](void* data) { NSShaper::FT_Done_Face_With_Library(data); });
return pointer->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_GetFaceInfo(JSSmart<CJSValue> face)
{
CExternalPointerJS result;
if (!NSShaper::FT_GetFaceInfo(RAW_POINTER(face), &result))
return CJSContext::createNull();
return CJSContext::createUint8Array(result.Data, result.Len, false);
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Load_Glyph(JSSmart<CJSValue> face, JSSmart<CJSValue> gid, JSSmart<CJSValue> mode)
{
return CJSContext::createInt(NSShaper::FT_Load_Glyph(RAW_POINTER(face), gid->toUInt32(), mode->toInt32()));
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Get_Glyph_Measure_Params(JSSmart<CJSValue> face, JSSmart<CJSValue> is_vector)
{
CExternalPointerJS result;
if (!NSShaper::FT_Get_Glyph_Measure_Params(RAW_POINTER(face), is_vector->toBool(), &result))
return CJSContext::createNull();
return CJSContext::createUint8Array(result.Data, result.Len, false);
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Get_Glyph_Render_Params(JSSmart<CJSValue> face, JSSmart<CJSValue> render_mode)
{
CExternalPointerJS result;
if (!NSShaper::FT_Get_Glyph_Render_Params(RAW_POINTER(face), render_mode->toInt32(), &result))
return CJSContext::createNull();
return CJSContext::createUint8Array(result.Data, result.Len, false);
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Get_Glyph_Render_Buffer(JSSmart<CJSValue> face, JSSmart<CJSValue> size)
{
void* Data = NSShaper::FT_Get_Glyph_Render_Buffer(RAW_POINTER(face));
int nSize = size->toInt32();
int nSizeMax = NSShaper::FT_Get_Glyph_Render_BufferSize(RAW_POINTER(face));
if (nSize > nSizeMax)
nSize = nSizeMax;
return CJSContext::createUint8Array((unsigned char*)Data, nSize, true);
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Set_Transform(JSSmart<CJSValue> face, JSSmart<CJSValue> xx, JSSmart<CJSValue> yx, JSSmart<CJSValue> xy, JSSmart<CJSValue> yy)
{
NSShaper::FT_Set_Transform(RAW_POINTER(face), xx->toInt32(), yx->toInt32(), xy->toInt32(), yy->toInt32());
return CJSContext::createUndefined();
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_Set_Char_Size(JSSmart<CJSValue> face, JSSmart<CJSValue> char_width, JSSmart<CJSValue> char_height, JSSmart<CJSValue> hres, JSSmart<CJSValue> vres)
{
return CJSContext::createInt(NSShaper::FT_Set_Char_Size(RAW_POINTER(face), char_width->toInt32(), char_height->toInt32(), hres->toUInt32(), vres->toUInt32()));
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_SetCMapForCharCode(JSSmart<CJSValue> face, JSSmart<CJSValue> unicode)
{
return CJSContext::createUInt(NSShaper::FT_SetCMapForCharCode(RAW_POINTER(face), unicode->toUInt32()));
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_GetKerningX(JSSmart<CJSValue> face, JSSmart<CJSValue> gid1, JSSmart<CJSValue> gid2)
{
return CJSContext::createInt(NSShaper::FT_GetKerningX(RAW_POINTER(face), gid1->toUInt32(), gid2->toUInt32()));
}
JSSmart<CJSValue> CTextMeasurerEmbed::FT_GetFaceMaxAdvanceX(JSSmart<CJSValue> face)
{
return CJSContext::createInt(NSShaper::FT_GetFaceMaxAdvanceX(RAW_POINTER(face)));
}
#ifdef SUPPORT_HARFBUZZ_SHAPER
JSSmart<CJSValue> CTextMeasurerEmbed::HB_LanguageFromString(JSSmart<CJSValue> language_bcp_47)
{
void* Data = NSShaper::HB_LanguageFromString(language_bcp_47->toStringA());
CPointerEmbedObject* pObject = new CPointerEmbedObject(Data, NSPointerObjectDeleters::EmptyDeleter);
return pObject->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::HB_ShapeText(JSSmart<CJSValue> face, JSSmart<CJSValue> font, JSSmart<CJSValue> text,
JSSmart<CJSValue> nFeatures, JSSmart<CJSValue> nScript, JSSmart<CJSValue> nDirection, JSSmart<CJSValue> nLanguage)
{
CPointerEmbedObject* pFont = POINTER_OBJECT(font);
CExternalPointerJS result;
JSSmart<CJSTypedArray> typedArray = text->toTypedArray();
CJSDataBuffer buffer = typedArray->getData();
char* pText = (char*)buffer.Data;
NSShaper::HB_ShapeText(RAW_POINTER(face), pFont->Data, pText,
nFeatures->toUInt32(), nScript->toUInt32(), nDirection->toUInt32(), RAW_POINTER(nLanguage),
&result, true);
if (buffer.IsExternalize)
buffer.Free();
if (NULL == result.Data)
return CJSContext::createNull();
return CJSContext::createUint8Array(result.Data, result.Len, false);
}
JSSmart<CJSValue> CTextMeasurerEmbed::HB_FontMalloc()
{
CPointerEmbedObject* pObject = new CPointerEmbedObject(NULL, [](void* data) { NSShaper::HB_FontFree(data); });
return pObject->createObject();
}
JSSmart<CJSValue> CTextMeasurerEmbed::HB_FontFree(JSSmart<CJSValue> font)
{
CPointerEmbedObject* pFont = POINTER_OBJECT(font);
pFont->Free();
return CJSContext::createUndefined();
}
#endif
JSSmart<CJSValue> CTextMeasurerEmbed::Hyphen_SetCacheSize(JSSmart<CJSValue> size)
{
((NSHyphen::CEngine*)m_hyphen_engine)->SetCacheSize(size->toInt32());
return CJSContext::createUndefined();
}
inline int GetUtf8SymbolLen(const unsigned char& c)
{
if (0x00 == (c & 0x80))
return 1;
else if (0x00 == (c & 0x20))
return 2;
else if (0x00 == (c & 0x10))
return 3;
else if (0x00 == (c & 0x0F))
return 4;
else if (0x00 == (c & 0x08))
return 4;
else if (0x00 == (c & 0x04))
return 5;
return 6;
}
JSSmart<CJSValue> CTextMeasurerEmbed::Hyphen_Word(JSSmart<CJSValue> lang, JSSmart<CJSValue> word)
{
std::string sWord = word->toStringA();
const char* curUnicode = sWord.c_str();
char* result = ((NSHyphen::CEngine*)m_hyphen_engine)->Process(lang->toInt32(), curUnicode, (int)sWord.length());
if (!result)
return CJSContext::createNull();
int count = 0;
int pos = 0;
while (result[pos] != 0)
{
if (1 == (result[pos] & 1))
++count;
++pos;
}
if (0 == count)
return CJSContext::createNull();
CJSArray* ret = CJSContext::createArray(count);
pos = 0;
count = 0;
while (result[pos] != 0)
{
if (1 == (result[pos] & 1))
ret->set(count++, CJSContext::createInt(pos + 1));
pos++;
}
return ret;
}
JSSmart<CJSValue> CTextMeasurerEmbed::Hyphen_IsDictionaryExist(JSSmart<CJSValue> lang)
{
return CJSContext::createBool(((NSHyphen::CEngine*)m_hyphen_engine)->IsDictionaryExist(lang->toInt32()));
}